switch-parental-controls 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- switch_parental_controls-0.1.0/LICENSE +21 -0
- switch_parental_controls-0.1.0/PKG-INFO +219 -0
- switch_parental_controls-0.1.0/README.md +200 -0
- switch_parental_controls-0.1.0/pyproject.toml +39 -0
- switch_parental_controls-0.1.0/setup.cfg +4 -0
- switch_parental_controls-0.1.0/src/nintendo_mcp/__init__.py +1 -0
- switch_parental_controls-0.1.0/src/nintendo_mcp/__main__.py +13 -0
- switch_parental_controls-0.1.0/src/nintendo_mcp/applications.py +181 -0
- switch_parental_controls-0.1.0/src/nintendo_mcp/auth.py +155 -0
- switch_parental_controls-0.1.0/src/nintendo_mcp/devices.py +808 -0
- switch_parental_controls-0.1.0/src/nintendo_mcp/models.py +326 -0
- switch_parental_controls-0.1.0/src/nintendo_mcp/players.py +194 -0
- switch_parental_controls-0.1.0/src/nintendo_mcp/server.py +100 -0
- switch_parental_controls-0.1.0/src/nintendo_mcp/utils.py +82 -0
- switch_parental_controls-0.1.0/src/switch_parental_controls.egg-info/PKG-INFO +219 -0
- switch_parental_controls-0.1.0/src/switch_parental_controls.egg-info/SOURCES.txt +23 -0
- switch_parental_controls-0.1.0/src/switch_parental_controls.egg-info/dependency_links.txt +1 -0
- switch_parental_controls-0.1.0/src/switch_parental_controls.egg-info/entry_points.txt +2 -0
- switch_parental_controls-0.1.0/src/switch_parental_controls.egg-info/requires.txt +10 -0
- switch_parental_controls-0.1.0/src/switch_parental_controls.egg-info/top_level.txt +1 -0
- switch_parental_controls-0.1.0/tests/test_applications.py +156 -0
- switch_parental_controls-0.1.0/tests/test_auth.py +108 -0
- switch_parental_controls-0.1.0/tests/test_devices.py +587 -0
- switch_parental_controls-0.1.0/tests/test_integration.py +132 -0
- switch_parental_controls-0.1.0/tests/test_players.py +153 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Daniel Schroeder
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: switch-parental-controls
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Nintendo Switch Parental Controls
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: mcp[cli]>=1.0.0
|
|
10
|
+
Requires-Dist: pydantic>=2.0.0
|
|
11
|
+
Requires-Dist: pynintendoparental>=2.3.4
|
|
12
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
|
16
|
+
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
17
|
+
Requires-Dist: python-dotenv>=1.0.0; extra == "dev"
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# Nintendo Switch Parental Controls
|
|
21
|
+
|
|
22
|
+
An [MCP (Model Context Protocol)](https://modelcontextprotocol.io) server that exposes Nintendo Switch Parental Controls as AI-accessible tools. It wraps the [`pynintendoparental`](https://github.com/pantherale0/pynintendoparental) library and allows AI assistants to monitor and manage parental control settings on Nintendo Switch devices.
|
|
23
|
+
|
|
24
|
+
## Features
|
|
25
|
+
|
|
26
|
+
- **Authentication**: Interactive Nintendo OAuth login flow via MCP tools, or pre-configured session token
|
|
27
|
+
- **Device monitoring**: List devices, view playtime, remaining time, sync status
|
|
28
|
+
- **Playtime controls**: Set daily limits, add extra time, configure per-day-of-week schedules
|
|
29
|
+
- **Bedtime controls**: Set bedtime alarms and end times
|
|
30
|
+
- **Restriction controls**: Set restriction mode (forced termination vs. alarm), content restriction levels
|
|
31
|
+
- **Player tracking**: View player profiles and today's app usage
|
|
32
|
+
- **Application management**: List apps, manage the allow-list (bypass content restrictions)
|
|
33
|
+
|
|
34
|
+
## Prerequisites
|
|
35
|
+
|
|
36
|
+
- [uv](https://docs.astral.sh/uv/) — install once, no Python or clone needed:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Authentication: Getting Your Nintendo Session Token
|
|
43
|
+
|
|
44
|
+
The server requires a Nintendo session token to access the Parental Controls API. You can obtain one in two ways:
|
|
45
|
+
|
|
46
|
+
### Method 1: Interactive MCP Tool (Recommended for first-time setup)
|
|
47
|
+
|
|
48
|
+
1. Start the MCP server (see below)
|
|
49
|
+
2. Ask your AI assistant to call `nintendo_get_login_url`
|
|
50
|
+
3. Open the returned URL in your browser
|
|
51
|
+
4. Log in with your Nintendo Account
|
|
52
|
+
5. On the "Select this person" page, **right-click** the "Select this person" button and copy the link
|
|
53
|
+
6. Ask your AI assistant to call `nintendo_complete_login` with the copied URL
|
|
54
|
+
7. The tool will return your session token — **save it!**
|
|
55
|
+
|
|
56
|
+
### Method 2: Manual (if you already have a token)
|
|
57
|
+
|
|
58
|
+
If you already have a session token, set it as an environment variable:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
export NINTENDO_SESSION_TOKEN="your-token-here"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Saving Your Token
|
|
65
|
+
|
|
66
|
+
Once you have a session token, add it to your environment so you don't need to log in again:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Add to your shell profile (~/.zshrc, ~/.bashrc, etc.)
|
|
70
|
+
export NINTENDO_SESSION_TOKEN="your-token-here"
|
|
71
|
+
|
|
72
|
+
# Or create a .env file (never commit this!)
|
|
73
|
+
echo 'NINTENDO_SESSION_TOKEN=your-token-here' >> .env
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
> **Note**: Session tokens can expire. If you get authentication errors, repeat the login flow.
|
|
77
|
+
|
|
78
|
+
## Running the Server
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
uvx --from switch-parental-controls mcp
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
No clone or install required — `uvx` fetches the package from PyPI and runs it in an isolated environment.
|
|
85
|
+
|
|
86
|
+
### Environment Variables
|
|
87
|
+
|
|
88
|
+
| Variable | Required | Default | Description |
|
|
89
|
+
| ------------------------ | -------- | --------------- | --------------------------------------- |
|
|
90
|
+
| `NINTENDO_SESSION_TOKEN` | No\* | — | Nintendo session token |
|
|
91
|
+
| `NINTENDO_TIMEZONE` | No | `Europe/London` | IANA timezone (e.g. `America/New_York`) |
|
|
92
|
+
| `NINTENDO_LANG` | No | `en-GB` | Language code (e.g. `en-US`) |
|
|
93
|
+
|
|
94
|
+
\*Required for any tool that accesses Nintendo data, unless you use the interactive login tools.
|
|
95
|
+
|
|
96
|
+
### MCP Client Configuration
|
|
97
|
+
|
|
98
|
+
Add to your MCP client configuration (e.g. Claude Desktop `claude_desktop_config.json`):
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"mcpServers": {
|
|
103
|
+
"nintendo": {
|
|
104
|
+
"command": "uvx",
|
|
105
|
+
"args": ["--from", "switch-parental-controls", "mcp"],
|
|
106
|
+
"env": {
|
|
107
|
+
"NINTENDO_SESSION_TOKEN": "your-token-here",
|
|
108
|
+
"NINTENDO_TIMEZONE": "America/New_York",
|
|
109
|
+
"NINTENDO_LANG": "en-US"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Available Tools
|
|
117
|
+
|
|
118
|
+
### Authentication
|
|
119
|
+
|
|
120
|
+
| Tool | Description |
|
|
121
|
+
| ------------------------- | ------------------------------------------------------------- |
|
|
122
|
+
| `nintendo_get_login_url` | Generate the Nintendo login URL and step-by-step instructions |
|
|
123
|
+
| `nintendo_complete_login` | Complete login with the redirect URL from the browser |
|
|
124
|
+
|
|
125
|
+
### Devices
|
|
126
|
+
|
|
127
|
+
| Tool | Description |
|
|
128
|
+
| ------------------------------ | ----------------------------------------------------------- |
|
|
129
|
+
| `nintendo_list_devices` | List all Nintendo Switch devices on the account |
|
|
130
|
+
| `nintendo_get_device` | Get detailed status for a specific device |
|
|
131
|
+
| `nintendo_get_today_summary` | Get today's usage summary for a device |
|
|
132
|
+
| `nintendo_get_monthly_summary` | Get monthly usage summary (optionally for a specific month) |
|
|
133
|
+
|
|
134
|
+
### Playtime Controls
|
|
135
|
+
|
|
136
|
+
| Tool | Description |
|
|
137
|
+
| ----------------------------------- | --------------------------------------------------------- |
|
|
138
|
+
| `nintendo_set_daily_playtime_limit` | Set the daily playtime limit (0-360 min, or -1 to remove) |
|
|
139
|
+
| `nintendo_add_extra_time` | Add extra playtime for today |
|
|
140
|
+
| `nintendo_set_timer_mode` | Switch between DAILY and EACH_DAY_OF_THE_WEEK modes |
|
|
141
|
+
| `nintendo_set_day_restrictions` | Set per-day playtime and bedtime restrictions |
|
|
142
|
+
|
|
143
|
+
### Restriction Controls
|
|
144
|
+
|
|
145
|
+
| Tool | Description |
|
|
146
|
+
| ---------------------------------------- | ---------------------------------------- |
|
|
147
|
+
| `nintendo_set_restriction_mode` | Set FORCED_TERMINATION or ALARM mode |
|
|
148
|
+
| `nintendo_set_content_restriction_level` | Set age-based content restrictions |
|
|
149
|
+
| `nintendo_set_bedtime_alarm` | Set the bedtime alarm time (16:00-23:00) |
|
|
150
|
+
| `nintendo_set_bedtime_end_time` | Set when bedtime ends (05:00-09:00) |
|
|
151
|
+
|
|
152
|
+
### Players
|
|
153
|
+
|
|
154
|
+
| Tool | Description |
|
|
155
|
+
| ----------------------- | ---------------------------------------------- |
|
|
156
|
+
| `nintendo_list_players` | List all players on a device |
|
|
157
|
+
| `nintendo_get_player` | Get player details including apps played today |
|
|
158
|
+
|
|
159
|
+
### Applications
|
|
160
|
+
|
|
161
|
+
| Tool | Description |
|
|
162
|
+
| ----------------------------- | --------------------------------------------------------- |
|
|
163
|
+
| `nintendo_list_applications` | List all tracked applications on a device |
|
|
164
|
+
| `nintendo_set_app_allow_list` | Add/remove an app from the content restriction allow-list |
|
|
165
|
+
|
|
166
|
+
## Development
|
|
167
|
+
|
|
168
|
+
Requires [mise](https://mise.jdx.dev/):
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# Install dependencies
|
|
172
|
+
mise run install
|
|
173
|
+
|
|
174
|
+
# Run tests
|
|
175
|
+
mise run test
|
|
176
|
+
|
|
177
|
+
# Run linter
|
|
178
|
+
mise run lint
|
|
179
|
+
|
|
180
|
+
# Fix lint issues
|
|
181
|
+
mise run lint-fix
|
|
182
|
+
|
|
183
|
+
# Open MCP Inspector (browser UI to test tools interactively)
|
|
184
|
+
mise run inspect
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### MCP Inspector
|
|
188
|
+
|
|
189
|
+
The `inspect` task launches the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) — a browser-based UI for testing MCP tools interactively without needing a full AI client.
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
mise run inspect
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
This opens the inspector connected to the nintendo_mcp server. You can call any tool directly from the UI, which is useful for testing the authentication flow and verifying tool responses.
|
|
196
|
+
|
|
197
|
+
### Testing with opencode locally
|
|
198
|
+
|
|
199
|
+
An example opencode config is provided at [`opencode.jsonc.example`](./opencode.jsonc.example). To use it:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# 1. Copy the example config
|
|
203
|
+
cp opencode.jsonc.example opencode.jsonc
|
|
204
|
+
|
|
205
|
+
# 2. Optionally set NINTENDO_SESSION_TOKEN in opencode.jsonc
|
|
206
|
+
|
|
207
|
+
# 3. Open opencode in this project directory — it will pick up the local config
|
|
208
|
+
opencode
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The local `opencode.jsonc` is gitignored so your session token stays private.
|
|
212
|
+
|
|
213
|
+
## CI
|
|
214
|
+
|
|
215
|
+
Tests run automatically on pull requests via GitHub Actions. See [`.github/workflows/test.yml`](.github/workflows/test.yml).
|
|
216
|
+
|
|
217
|
+
## License
|
|
218
|
+
|
|
219
|
+
MIT
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Nintendo Switch Parental Controls
|
|
2
|
+
|
|
3
|
+
An [MCP (Model Context Protocol)](https://modelcontextprotocol.io) server that exposes Nintendo Switch Parental Controls as AI-accessible tools. It wraps the [`pynintendoparental`](https://github.com/pantherale0/pynintendoparental) library and allows AI assistants to monitor and manage parental control settings on Nintendo Switch devices.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Authentication**: Interactive Nintendo OAuth login flow via MCP tools, or pre-configured session token
|
|
8
|
+
- **Device monitoring**: List devices, view playtime, remaining time, sync status
|
|
9
|
+
- **Playtime controls**: Set daily limits, add extra time, configure per-day-of-week schedules
|
|
10
|
+
- **Bedtime controls**: Set bedtime alarms and end times
|
|
11
|
+
- **Restriction controls**: Set restriction mode (forced termination vs. alarm), content restriction levels
|
|
12
|
+
- **Player tracking**: View player profiles and today's app usage
|
|
13
|
+
- **Application management**: List apps, manage the allow-list (bypass content restrictions)
|
|
14
|
+
|
|
15
|
+
## Prerequisites
|
|
16
|
+
|
|
17
|
+
- [uv](https://docs.astral.sh/uv/) — install once, no Python or clone needed:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Authentication: Getting Your Nintendo Session Token
|
|
24
|
+
|
|
25
|
+
The server requires a Nintendo session token to access the Parental Controls API. You can obtain one in two ways:
|
|
26
|
+
|
|
27
|
+
### Method 1: Interactive MCP Tool (Recommended for first-time setup)
|
|
28
|
+
|
|
29
|
+
1. Start the MCP server (see below)
|
|
30
|
+
2. Ask your AI assistant to call `nintendo_get_login_url`
|
|
31
|
+
3. Open the returned URL in your browser
|
|
32
|
+
4. Log in with your Nintendo Account
|
|
33
|
+
5. On the "Select this person" page, **right-click** the "Select this person" button and copy the link
|
|
34
|
+
6. Ask your AI assistant to call `nintendo_complete_login` with the copied URL
|
|
35
|
+
7. The tool will return your session token — **save it!**
|
|
36
|
+
|
|
37
|
+
### Method 2: Manual (if you already have a token)
|
|
38
|
+
|
|
39
|
+
If you already have a session token, set it as an environment variable:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
export NINTENDO_SESSION_TOKEN="your-token-here"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Saving Your Token
|
|
46
|
+
|
|
47
|
+
Once you have a session token, add it to your environment so you don't need to log in again:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Add to your shell profile (~/.zshrc, ~/.bashrc, etc.)
|
|
51
|
+
export NINTENDO_SESSION_TOKEN="your-token-here"
|
|
52
|
+
|
|
53
|
+
# Or create a .env file (never commit this!)
|
|
54
|
+
echo 'NINTENDO_SESSION_TOKEN=your-token-here' >> .env
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
> **Note**: Session tokens can expire. If you get authentication errors, repeat the login flow.
|
|
58
|
+
|
|
59
|
+
## Running the Server
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
uvx --from switch-parental-controls mcp
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
No clone or install required — `uvx` fetches the package from PyPI and runs it in an isolated environment.
|
|
66
|
+
|
|
67
|
+
### Environment Variables
|
|
68
|
+
|
|
69
|
+
| Variable | Required | Default | Description |
|
|
70
|
+
| ------------------------ | -------- | --------------- | --------------------------------------- |
|
|
71
|
+
| `NINTENDO_SESSION_TOKEN` | No\* | — | Nintendo session token |
|
|
72
|
+
| `NINTENDO_TIMEZONE` | No | `Europe/London` | IANA timezone (e.g. `America/New_York`) |
|
|
73
|
+
| `NINTENDO_LANG` | No | `en-GB` | Language code (e.g. `en-US`) |
|
|
74
|
+
|
|
75
|
+
\*Required for any tool that accesses Nintendo data, unless you use the interactive login tools.
|
|
76
|
+
|
|
77
|
+
### MCP Client Configuration
|
|
78
|
+
|
|
79
|
+
Add to your MCP client configuration (e.g. Claude Desktop `claude_desktop_config.json`):
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"mcpServers": {
|
|
84
|
+
"nintendo": {
|
|
85
|
+
"command": "uvx",
|
|
86
|
+
"args": ["--from", "switch-parental-controls", "mcp"],
|
|
87
|
+
"env": {
|
|
88
|
+
"NINTENDO_SESSION_TOKEN": "your-token-here",
|
|
89
|
+
"NINTENDO_TIMEZONE": "America/New_York",
|
|
90
|
+
"NINTENDO_LANG": "en-US"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Available Tools
|
|
98
|
+
|
|
99
|
+
### Authentication
|
|
100
|
+
|
|
101
|
+
| Tool | Description |
|
|
102
|
+
| ------------------------- | ------------------------------------------------------------- |
|
|
103
|
+
| `nintendo_get_login_url` | Generate the Nintendo login URL and step-by-step instructions |
|
|
104
|
+
| `nintendo_complete_login` | Complete login with the redirect URL from the browser |
|
|
105
|
+
|
|
106
|
+
### Devices
|
|
107
|
+
|
|
108
|
+
| Tool | Description |
|
|
109
|
+
| ------------------------------ | ----------------------------------------------------------- |
|
|
110
|
+
| `nintendo_list_devices` | List all Nintendo Switch devices on the account |
|
|
111
|
+
| `nintendo_get_device` | Get detailed status for a specific device |
|
|
112
|
+
| `nintendo_get_today_summary` | Get today's usage summary for a device |
|
|
113
|
+
| `nintendo_get_monthly_summary` | Get monthly usage summary (optionally for a specific month) |
|
|
114
|
+
|
|
115
|
+
### Playtime Controls
|
|
116
|
+
|
|
117
|
+
| Tool | Description |
|
|
118
|
+
| ----------------------------------- | --------------------------------------------------------- |
|
|
119
|
+
| `nintendo_set_daily_playtime_limit` | Set the daily playtime limit (0-360 min, or -1 to remove) |
|
|
120
|
+
| `nintendo_add_extra_time` | Add extra playtime for today |
|
|
121
|
+
| `nintendo_set_timer_mode` | Switch between DAILY and EACH_DAY_OF_THE_WEEK modes |
|
|
122
|
+
| `nintendo_set_day_restrictions` | Set per-day playtime and bedtime restrictions |
|
|
123
|
+
|
|
124
|
+
### Restriction Controls
|
|
125
|
+
|
|
126
|
+
| Tool | Description |
|
|
127
|
+
| ---------------------------------------- | ---------------------------------------- |
|
|
128
|
+
| `nintendo_set_restriction_mode` | Set FORCED_TERMINATION or ALARM mode |
|
|
129
|
+
| `nintendo_set_content_restriction_level` | Set age-based content restrictions |
|
|
130
|
+
| `nintendo_set_bedtime_alarm` | Set the bedtime alarm time (16:00-23:00) |
|
|
131
|
+
| `nintendo_set_bedtime_end_time` | Set when bedtime ends (05:00-09:00) |
|
|
132
|
+
|
|
133
|
+
### Players
|
|
134
|
+
|
|
135
|
+
| Tool | Description |
|
|
136
|
+
| ----------------------- | ---------------------------------------------- |
|
|
137
|
+
| `nintendo_list_players` | List all players on a device |
|
|
138
|
+
| `nintendo_get_player` | Get player details including apps played today |
|
|
139
|
+
|
|
140
|
+
### Applications
|
|
141
|
+
|
|
142
|
+
| Tool | Description |
|
|
143
|
+
| ----------------------------- | --------------------------------------------------------- |
|
|
144
|
+
| `nintendo_list_applications` | List all tracked applications on a device |
|
|
145
|
+
| `nintendo_set_app_allow_list` | Add/remove an app from the content restriction allow-list |
|
|
146
|
+
|
|
147
|
+
## Development
|
|
148
|
+
|
|
149
|
+
Requires [mise](https://mise.jdx.dev/):
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# Install dependencies
|
|
153
|
+
mise run install
|
|
154
|
+
|
|
155
|
+
# Run tests
|
|
156
|
+
mise run test
|
|
157
|
+
|
|
158
|
+
# Run linter
|
|
159
|
+
mise run lint
|
|
160
|
+
|
|
161
|
+
# Fix lint issues
|
|
162
|
+
mise run lint-fix
|
|
163
|
+
|
|
164
|
+
# Open MCP Inspector (browser UI to test tools interactively)
|
|
165
|
+
mise run inspect
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### MCP Inspector
|
|
169
|
+
|
|
170
|
+
The `inspect` task launches the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) — a browser-based UI for testing MCP tools interactively without needing a full AI client.
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
mise run inspect
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
This opens the inspector connected to the nintendo_mcp server. You can call any tool directly from the UI, which is useful for testing the authentication flow and verifying tool responses.
|
|
177
|
+
|
|
178
|
+
### Testing with opencode locally
|
|
179
|
+
|
|
180
|
+
An example opencode config is provided at [`opencode.jsonc.example`](./opencode.jsonc.example). To use it:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# 1. Copy the example config
|
|
184
|
+
cp opencode.jsonc.example opencode.jsonc
|
|
185
|
+
|
|
186
|
+
# 2. Optionally set NINTENDO_SESSION_TOKEN in opencode.jsonc
|
|
187
|
+
|
|
188
|
+
# 3. Open opencode in this project directory — it will pick up the local config
|
|
189
|
+
opencode
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The local `opencode.jsonc` is gitignored so your session token stays private.
|
|
193
|
+
|
|
194
|
+
## CI
|
|
195
|
+
|
|
196
|
+
Tests run automatically on pull requests via GitHub Actions. See [`.github/workflows/test.yml`](.github/workflows/test.yml).
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
MIT
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "switch-parental-controls"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Nintendo Switch Parental Controls"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.12"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"mcp[cli]>=1.0.0",
|
|
14
|
+
"pydantic>=2.0.0",
|
|
15
|
+
"pynintendoparental>=2.3.4",
|
|
16
|
+
"aiohttp>=3.9.0",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.optional-dependencies]
|
|
20
|
+
dev = ["pytest>=8.0.0", "pytest-asyncio>=0.23.0", "ruff>=0.4.0", "python-dotenv>=1.0.0"]
|
|
21
|
+
|
|
22
|
+
[project.scripts]
|
|
23
|
+
mcp = "nintendo_mcp.server:main"
|
|
24
|
+
|
|
25
|
+
[tool.setuptools.packages.find]
|
|
26
|
+
where = ["src"]
|
|
27
|
+
|
|
28
|
+
[tool.pytest.ini_options]
|
|
29
|
+
asyncio_mode = "auto"
|
|
30
|
+
asyncio_default_fixture_loop_scope = "module"
|
|
31
|
+
testpaths = ["tests"]
|
|
32
|
+
markers = ["integration: mark a test as an integration test requiring real Nintendo API credentials"]
|
|
33
|
+
|
|
34
|
+
[tool.ruff]
|
|
35
|
+
line-length = 120
|
|
36
|
+
target-version = "py312"
|
|
37
|
+
|
|
38
|
+
[tool.ruff.lint]
|
|
39
|
+
select = ["E", "F", "I", "UP"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Nintendo Switch Parental Controls MCP Server."""
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Entry point for running the Nintendo MCP server as a module.
|
|
2
|
+
|
|
3
|
+
This file ensures the server is always imported as 'nintendo_mcp.server'
|
|
4
|
+
(never as '__main__'), so tool registrations from all submodules land on
|
|
5
|
+
the same FastMCP instance that gets served.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python -m nintendo_mcp
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from nintendo_mcp.server import main
|
|
12
|
+
|
|
13
|
+
main()
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""Application tools for the Nintendo MCP server.
|
|
2
|
+
|
|
3
|
+
Provides tools to read application information and manage the allow-list
|
|
4
|
+
(safe launch settings) for games on Nintendo Switch devices.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from mcp.server.fastmcp import Context
|
|
8
|
+
from pynintendoparental.enum import SafeLaunchSetting
|
|
9
|
+
|
|
10
|
+
from nintendo_mcp.models import DeviceInput, ResponseFormat, SetAppAllowListInput
|
|
11
|
+
from nintendo_mcp.server import _state, mcp
|
|
12
|
+
from nintendo_mcp.utils import format_minutes, handle_error, require_client, to_json
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _app_to_dict(app) -> dict:
|
|
16
|
+
"""Convert an Application object to a serializable dictionary."""
|
|
17
|
+
return {
|
|
18
|
+
"application_id": app.application_id,
|
|
19
|
+
"name": app.name,
|
|
20
|
+
"image_url": app.image_url,
|
|
21
|
+
"today_time_played_minutes": app.today_time_played,
|
|
22
|
+
"allow_list_status": str(app.safe_launch_setting),
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@mcp.tool(
|
|
27
|
+
name="nintendo_list_applications",
|
|
28
|
+
annotations={
|
|
29
|
+
"title": "List Applications on Device",
|
|
30
|
+
"readOnlyHint": True,
|
|
31
|
+
"destructiveHint": False,
|
|
32
|
+
"idempotentHint": True,
|
|
33
|
+
"openWorldHint": True,
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
async def nintendo_list_applications(params: DeviceInput, ctx: Context) -> str:
|
|
37
|
+
"""List all applications (games) tracked on a Nintendo Switch device.
|
|
38
|
+
|
|
39
|
+
Returns all games and applications that have been played on the device,
|
|
40
|
+
including their names, today's playtime, and allow-list status.
|
|
41
|
+
|
|
42
|
+
The allow-list status indicates whether an application can bypass content
|
|
43
|
+
restrictions (ALLOW) or is subject to normal restrictions (NONE).
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
params (DeviceInput): Validated input containing:
|
|
47
|
+
- device_id (str): The unique device ID (from nintendo_list_devices).
|
|
48
|
+
- response_format (str): 'markdown' or 'json' (default: 'markdown').
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
str: List of applications with their status, or an error message.
|
|
52
|
+
|
|
53
|
+
Success response (JSON):
|
|
54
|
+
{
|
|
55
|
+
"count": int,
|
|
56
|
+
"device_name": str,
|
|
57
|
+
"applications": [
|
|
58
|
+
{
|
|
59
|
+
"application_id": str,
|
|
60
|
+
"name": str,
|
|
61
|
+
"image_url": str,
|
|
62
|
+
"today_time_played_minutes": int,
|
|
63
|
+
"allow_list_status": str # "NONE" or "ALLOW"
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
Error Handling:
|
|
69
|
+
- Returns "Error: Not authenticated..." if no session token is configured.
|
|
70
|
+
- Returns "No applications found..." if no apps have been tracked.
|
|
71
|
+
"""
|
|
72
|
+
err = require_client(_state.get("client"))
|
|
73
|
+
if err:
|
|
74
|
+
return err
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
client = _state["client"]
|
|
78
|
+
device = client.devices.get(params.device_id)
|
|
79
|
+
if device is None:
|
|
80
|
+
return (
|
|
81
|
+
f"Error: Device '{params.device_id}' not found. "
|
|
82
|
+
"Use nintendo_list_devices to see available device IDs."
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
await device.update()
|
|
86
|
+
apps = list(device.applications.values())
|
|
87
|
+
|
|
88
|
+
if not apps:
|
|
89
|
+
return f"No applications tracked on device '{device.name}'."
|
|
90
|
+
|
|
91
|
+
if params.response_format == ResponseFormat.JSON:
|
|
92
|
+
return to_json(
|
|
93
|
+
{
|
|
94
|
+
"count": len(apps),
|
|
95
|
+
"device_name": device.name,
|
|
96
|
+
"applications": [_app_to_dict(a) for a in apps],
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
lines = [f"# Applications on {device.name}", ""]
|
|
101
|
+
for app in apps:
|
|
102
|
+
allow_status = str(app.safe_launch_setting)
|
|
103
|
+
allow_label = "✓ Allow-listed" if allow_status == "ALLOW" else "Normal restrictions"
|
|
104
|
+
lines.append(f"## {app.name}")
|
|
105
|
+
lines.append(f"- **App ID**: `{app.application_id}`")
|
|
106
|
+
lines.append(f"- **Today's playtime**: {format_minutes(app.today_time_played)}")
|
|
107
|
+
lines.append(f"- **Allow-list**: {allow_label}")
|
|
108
|
+
lines.append("")
|
|
109
|
+
return "\n".join(lines)
|
|
110
|
+
|
|
111
|
+
except Exception as e:
|
|
112
|
+
return handle_error(e)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@mcp.tool(
|
|
116
|
+
name="nintendo_set_app_allow_list",
|
|
117
|
+
annotations={
|
|
118
|
+
"title": "Set Application Allow-List Status",
|
|
119
|
+
"readOnlyHint": False,
|
|
120
|
+
"destructiveHint": False,
|
|
121
|
+
"idempotentHint": True,
|
|
122
|
+
"openWorldHint": True,
|
|
123
|
+
},
|
|
124
|
+
)
|
|
125
|
+
async def nintendo_set_app_allow_list(params: SetAppAllowListInput, ctx: Context) -> str:
|
|
126
|
+
"""Set whether an application can bypass content restrictions on a Nintendo Switch device.
|
|
127
|
+
|
|
128
|
+
Adding an application to the allow-list lets it be launched regardless of the
|
|
129
|
+
device's content restriction level (age rating filter). This is useful for
|
|
130
|
+
educational apps or games that are safe but might be blocked by strict settings.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
params (SetAppAllowListInput): Validated input containing:
|
|
134
|
+
- device_id (str): The unique device ID (from nintendo_list_devices).
|
|
135
|
+
- application_id (str): The unique app ID (from nintendo_list_applications).
|
|
136
|
+
- allow (bool): True to add to allow-list, False to remove from allow-list.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
str: Confirmation message, or an error message.
|
|
140
|
+
|
|
141
|
+
Error Handling:
|
|
142
|
+
- Returns "Error: Not authenticated..." if no session token is configured.
|
|
143
|
+
- Returns "Error: ..." if the application_id is not found on the device.
|
|
144
|
+
"""
|
|
145
|
+
err = require_client(_state.get("client"))
|
|
146
|
+
if err:
|
|
147
|
+
return err
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
client = _state["client"]
|
|
151
|
+
device = client.devices.get(params.device_id)
|
|
152
|
+
if device is None:
|
|
153
|
+
return (
|
|
154
|
+
f"Error: Device '{params.device_id}' not found. "
|
|
155
|
+
"Use nintendo_list_devices to see available device IDs."
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
await device.update()
|
|
159
|
+
try:
|
|
160
|
+
app = device.get_application(params.application_id)
|
|
161
|
+
except ValueError:
|
|
162
|
+
return (
|
|
163
|
+
f"Error: Application '{params.application_id}' not found on device '{device.name}'. "
|
|
164
|
+
"Use nintendo_list_applications to see available application IDs."
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
setting = SafeLaunchSetting.ALLOW if params.allow else SafeLaunchSetting.NONE
|
|
168
|
+
await app.set_safe_launch_setting(setting)
|
|
169
|
+
|
|
170
|
+
if params.allow:
|
|
171
|
+
return (
|
|
172
|
+
f"✓ '{app.name}' added to the allow-list on '{device.name}'. "
|
|
173
|
+
"It can now bypass content restrictions."
|
|
174
|
+
)
|
|
175
|
+
return (
|
|
176
|
+
f"✓ '{app.name}' removed from the allow-list on '{device.name}'. "
|
|
177
|
+
"It is now subject to normal content restrictions."
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
except Exception as e:
|
|
181
|
+
return handle_error(e)
|