classicchess-mcp 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.
- classicchess_mcp-0.1.0/LICENSE +21 -0
- classicchess_mcp-0.1.0/PKG-INFO +138 -0
- classicchess_mcp-0.1.0/README.md +119 -0
- classicchess_mcp-0.1.0/classicchess_mcp/__init__.py +1 -0
- classicchess_mcp-0.1.0/classicchess_mcp/__main__.py +138 -0
- classicchess_mcp-0.1.0/classicchess_mcp.egg-info/PKG-INFO +138 -0
- classicchess_mcp-0.1.0/classicchess_mcp.egg-info/SOURCES.txt +10 -0
- classicchess_mcp-0.1.0/classicchess_mcp.egg-info/dependency_links.txt +1 -0
- classicchess_mcp-0.1.0/classicchess_mcp.egg-info/entry_points.txt +2 -0
- classicchess_mcp-0.1.0/classicchess_mcp.egg-info/top_level.txt +1 -0
- classicchess_mcp-0.1.0/pyproject.toml +32 -0
- classicchess_mcp-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Christian Walls
|
|
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,138 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: classicchess-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Classic Chess stdio MCP client for its hosted study-plan server
|
|
5
|
+
Author: Christian Walls
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://classicchess.com/api/
|
|
8
|
+
Project-URL: Documentation, https://classicchess.com/api/docs/integration/#ai-agents
|
|
9
|
+
Project-URL: Download, https://classicchess.com/api/downloads/classicchess-mcp.zip
|
|
10
|
+
Project-URL: Support, https://classicchess.com/support/
|
|
11
|
+
Keywords: chess,mcp,study-plans
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# Classic Chess MCP
|
|
21
|
+
|
|
22
|
+
Connect an assistant to Classic Chess to find games, read PGNs, biographies and
|
|
23
|
+
published annotations, and save study plans with notes and shared progress.
|
|
24
|
+
The hosted MCP URL is `https://classicchess.com/mcp/`.
|
|
25
|
+
|
|
26
|
+
## Release availability
|
|
27
|
+
|
|
28
|
+
Version 0.1.0 is a development preview of the relay. The Python package is
|
|
29
|
+
available on PyPI; the hosted MCP endpoint and browser setup guide are awaiting
|
|
30
|
+
the website deployment. Until then, use a local Classic Chess development
|
|
31
|
+
server with `--base-url http://127.0.0.1:PORT`, replacing `PORT` with its port.
|
|
32
|
+
|
|
33
|
+
## Browser users
|
|
34
|
+
|
|
35
|
+
Claude and ChatGPT browser users connect to the hosted URL and sign in with
|
|
36
|
+
their own Classic Chess account. Follow the
|
|
37
|
+
[browser setup guide](https://classicchess.com/api/docs/integration/#ai-agents)
|
|
38
|
+
for the current menus, account-plan requirements and workspace permissions.
|
|
39
|
+
Choose automatic OAuth client registration in Claude. ChatGPT's available
|
|
40
|
+
read and write actions depend on its plan and workspace permissions.
|
|
41
|
+
|
|
42
|
+
Public searches work without account access. Approve library read and write
|
|
43
|
+
access to save plans and read progress. Revoke the connection in Classic Chess
|
|
44
|
+
[Settings](https://classicchess.com/settings/). A Python installation is needed
|
|
45
|
+
only for the optional stdio relay below.
|
|
46
|
+
|
|
47
|
+
## Claude Code
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
claude mcp add --scope user --transport http classicchess https://classicchess.com/mcp/
|
|
51
|
+
claude mcp login classicchess
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Complete browser sign-in and approve library access. Enable Classic Chess in
|
|
55
|
+
a new session and ask, for example: “Build a five-game rook-endgame study plan
|
|
56
|
+
and save it to my account.”
|
|
57
|
+
|
|
58
|
+
## Codex
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
codex mcp add classicchess --url https://classicchess.com/mcp/
|
|
62
|
+
codex mcp login classicchess --scopes library:read,library:write
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Codex may start browser sign-in when adding the server. The login command also
|
|
66
|
+
lets you connect again later. Plans and progress belong to the account you
|
|
67
|
+
approve, and are shared with the Classic Chess website.
|
|
68
|
+
|
|
69
|
+
## Optional Python stdio relay
|
|
70
|
+
|
|
71
|
+
This MIT-licensed package needs Python 3.10 or later and has no runtime
|
|
72
|
+
dependencies. It relays newline-delimited MCP messages to the same server.
|
|
73
|
+
Install it from [PyPI](https://pypi.org/project/classicchess-mcp/) into a virtual
|
|
74
|
+
environment.
|
|
75
|
+
|
|
76
|
+
On Linux or macOS:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
python3 -m venv classicchess-mcp-env
|
|
80
|
+
classicchess-mcp-env/bin/python -m pip install classicchess-mcp
|
|
81
|
+
classicchess-mcp-env/bin/classicchess-mcp --help
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
On Windows:
|
|
85
|
+
|
|
86
|
+
```powershell
|
|
87
|
+
py -m venv classicchess-mcp-env
|
|
88
|
+
classicchess-mcp-env\Scripts\python -m pip install classicchess-mcp
|
|
89
|
+
classicchess-mcp-env\Scripts\classicchess-mcp --help
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
In a source checkout, install with `python3 -m pip install .` inside your virtual
|
|
93
|
+
environment. The distribution name is `classicchess-mcp`. The
|
|
94
|
+
[website source ZIP](https://classicchess.com/api/downloads/classicchess-mcp.zip)
|
|
95
|
+
will also be available after the website deployment.
|
|
96
|
+
|
|
97
|
+
Create a personal token in [Settings](https://classicchess.com/settings/) with
|
|
98
|
+
`library:read` and `library:write`, then configure your MCP client's private
|
|
99
|
+
environment:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"mcpServers": {
|
|
104
|
+
"classicchess": {
|
|
105
|
+
"command": "FULL_PATH_TO_CLASSICCHESS_MCP",
|
|
106
|
+
"env": {"CLASSICCHESS_API_TOKEN": "YOUR_PERSONAL_TOKEN"}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Replace `FULL_PATH_TO_CLASSICCHESS_MCP` with the absolute path to the executable
|
|
113
|
+
in the virtual environment above (`bin/classicchess-mcp` on Linux/macOS,
|
|
114
|
+
`Scripts\classicchess-mcp.exe` on Windows). Replace `YOUR_PERSONAL_TOKEN` only
|
|
115
|
+
in your client's private configuration. Revoke the token in Settings.
|
|
116
|
+
The relay writes only MCP messages to stdout and never prints credentials,
|
|
117
|
+
follows redirects or sends credentials to another origin.
|
|
118
|
+
|
|
119
|
+
For local development, pass `--base-url http://127.0.0.1:PORT`, replacing `PORT`
|
|
120
|
+
with your development server's port. The canonical HTTPS site and an explicitly
|
|
121
|
+
selected HTTP loopback origin are accepted.
|
|
122
|
+
|
|
123
|
+
## Tools and protocol
|
|
124
|
+
|
|
125
|
+
The nine tools are `search_players`, `search_games`, `get_game_pgn`,
|
|
126
|
+
`get_annotations`, `get_player_bio`, `create_plan`, `update_plan`, `mark_studied`
|
|
127
|
+
and `get_progress`. Plan creation accepts 1–100 ordered game slugs and original
|
|
128
|
+
notes. Reuse a stable idempotency key when retrying the same plan. `update_plan`
|
|
129
|
+
performs one operation per call: metadata, reorder, a note, add a game or
|
|
130
|
+
remove an item. Theme rules and evidence are at `/api/v1/public/themes/`.
|
|
131
|
+
|
|
132
|
+
The server uses stateless Streamable HTTP with JSON responses. It supports
|
|
133
|
+
protocol `2026-07-28` with per-request metadata and bound routing headers, plus
|
|
134
|
+
the initialization flows for `2025-11-25`, `2025-06-18` and `2025-03-26`.
|
|
135
|
+
It publishes OAuth discovery, supports dynamic client registration, requires
|
|
136
|
+
S256 PKCE and accepts native loopback callbacks when the issuer is production
|
|
137
|
+
HTTPS. Clients supporting fixed headers can use personal API tokens instead.
|
|
138
|
+
Source-attributed annotations retain their source and reuse terms.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Classic Chess MCP
|
|
2
|
+
|
|
3
|
+
Connect an assistant to Classic Chess to find games, read PGNs, biographies and
|
|
4
|
+
published annotations, and save study plans with notes and shared progress.
|
|
5
|
+
The hosted MCP URL is `https://classicchess.com/mcp/`.
|
|
6
|
+
|
|
7
|
+
## Release availability
|
|
8
|
+
|
|
9
|
+
Version 0.1.0 is a development preview of the relay. The Python package is
|
|
10
|
+
available on PyPI; the hosted MCP endpoint and browser setup guide are awaiting
|
|
11
|
+
the website deployment. Until then, use a local Classic Chess development
|
|
12
|
+
server with `--base-url http://127.0.0.1:PORT`, replacing `PORT` with its port.
|
|
13
|
+
|
|
14
|
+
## Browser users
|
|
15
|
+
|
|
16
|
+
Claude and ChatGPT browser users connect to the hosted URL and sign in with
|
|
17
|
+
their own Classic Chess account. Follow the
|
|
18
|
+
[browser setup guide](https://classicchess.com/api/docs/integration/#ai-agents)
|
|
19
|
+
for the current menus, account-plan requirements and workspace permissions.
|
|
20
|
+
Choose automatic OAuth client registration in Claude. ChatGPT's available
|
|
21
|
+
read and write actions depend on its plan and workspace permissions.
|
|
22
|
+
|
|
23
|
+
Public searches work without account access. Approve library read and write
|
|
24
|
+
access to save plans and read progress. Revoke the connection in Classic Chess
|
|
25
|
+
[Settings](https://classicchess.com/settings/). A Python installation is needed
|
|
26
|
+
only for the optional stdio relay below.
|
|
27
|
+
|
|
28
|
+
## Claude Code
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
claude mcp add --scope user --transport http classicchess https://classicchess.com/mcp/
|
|
32
|
+
claude mcp login classicchess
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Complete browser sign-in and approve library access. Enable Classic Chess in
|
|
36
|
+
a new session and ask, for example: “Build a five-game rook-endgame study plan
|
|
37
|
+
and save it to my account.”
|
|
38
|
+
|
|
39
|
+
## Codex
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
codex mcp add classicchess --url https://classicchess.com/mcp/
|
|
43
|
+
codex mcp login classicchess --scopes library:read,library:write
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Codex may start browser sign-in when adding the server. The login command also
|
|
47
|
+
lets you connect again later. Plans and progress belong to the account you
|
|
48
|
+
approve, and are shared with the Classic Chess website.
|
|
49
|
+
|
|
50
|
+
## Optional Python stdio relay
|
|
51
|
+
|
|
52
|
+
This MIT-licensed package needs Python 3.10 or later and has no runtime
|
|
53
|
+
dependencies. It relays newline-delimited MCP messages to the same server.
|
|
54
|
+
Install it from [PyPI](https://pypi.org/project/classicchess-mcp/) into a virtual
|
|
55
|
+
environment.
|
|
56
|
+
|
|
57
|
+
On Linux or macOS:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
python3 -m venv classicchess-mcp-env
|
|
61
|
+
classicchess-mcp-env/bin/python -m pip install classicchess-mcp
|
|
62
|
+
classicchess-mcp-env/bin/classicchess-mcp --help
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
On Windows:
|
|
66
|
+
|
|
67
|
+
```powershell
|
|
68
|
+
py -m venv classicchess-mcp-env
|
|
69
|
+
classicchess-mcp-env\Scripts\python -m pip install classicchess-mcp
|
|
70
|
+
classicchess-mcp-env\Scripts\classicchess-mcp --help
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
In a source checkout, install with `python3 -m pip install .` inside your virtual
|
|
74
|
+
environment. The distribution name is `classicchess-mcp`. The
|
|
75
|
+
[website source ZIP](https://classicchess.com/api/downloads/classicchess-mcp.zip)
|
|
76
|
+
will also be available after the website deployment.
|
|
77
|
+
|
|
78
|
+
Create a personal token in [Settings](https://classicchess.com/settings/) with
|
|
79
|
+
`library:read` and `library:write`, then configure your MCP client's private
|
|
80
|
+
environment:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"mcpServers": {
|
|
85
|
+
"classicchess": {
|
|
86
|
+
"command": "FULL_PATH_TO_CLASSICCHESS_MCP",
|
|
87
|
+
"env": {"CLASSICCHESS_API_TOKEN": "YOUR_PERSONAL_TOKEN"}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Replace `FULL_PATH_TO_CLASSICCHESS_MCP` with the absolute path to the executable
|
|
94
|
+
in the virtual environment above (`bin/classicchess-mcp` on Linux/macOS,
|
|
95
|
+
`Scripts\classicchess-mcp.exe` on Windows). Replace `YOUR_PERSONAL_TOKEN` only
|
|
96
|
+
in your client's private configuration. Revoke the token in Settings.
|
|
97
|
+
The relay writes only MCP messages to stdout and never prints credentials,
|
|
98
|
+
follows redirects or sends credentials to another origin.
|
|
99
|
+
|
|
100
|
+
For local development, pass `--base-url http://127.0.0.1:PORT`, replacing `PORT`
|
|
101
|
+
with your development server's port. The canonical HTTPS site and an explicitly
|
|
102
|
+
selected HTTP loopback origin are accepted.
|
|
103
|
+
|
|
104
|
+
## Tools and protocol
|
|
105
|
+
|
|
106
|
+
The nine tools are `search_players`, `search_games`, `get_game_pgn`,
|
|
107
|
+
`get_annotations`, `get_player_bio`, `create_plan`, `update_plan`, `mark_studied`
|
|
108
|
+
and `get_progress`. Plan creation accepts 1–100 ordered game slugs and original
|
|
109
|
+
notes. Reuse a stable idempotency key when retrying the same plan. `update_plan`
|
|
110
|
+
performs one operation per call: metadata, reorder, a note, add a game or
|
|
111
|
+
remove an item. Theme rules and evidence are at `/api/v1/public/themes/`.
|
|
112
|
+
|
|
113
|
+
The server uses stateless Streamable HTTP with JSON responses. It supports
|
|
114
|
+
protocol `2026-07-28` with per-request metadata and bound routing headers, plus
|
|
115
|
+
the initialization flows for `2025-11-25`, `2025-06-18` and `2025-03-26`.
|
|
116
|
+
It publishes OAuth discovery, supports dynamic client registration, requires
|
|
117
|
+
S256 PKCE and accepts native loopback callbacks when the issuer is production
|
|
118
|
+
HTTPS. Clients supporting fixed headers can use personal API tokens instead.
|
|
119
|
+
Source-attributed annotations retain their source and reuse terms.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Standard-library stdio transport for the Classic Chess hosted MCP server."""
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Relay bounded newline-delimited MCP messages to the authenticated HTTP server."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import base64
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from urllib.error import HTTPError
|
|
9
|
+
from urllib.error import URLError
|
|
10
|
+
from urllib.parse import urlsplit
|
|
11
|
+
from urllib.request import HTTPRedirectHandler
|
|
12
|
+
from urllib.request import Request
|
|
13
|
+
from urllib.request import build_opener
|
|
14
|
+
|
|
15
|
+
MAX_MESSAGE_BYTES = 262144
|
|
16
|
+
MAX_RESPONSE_BYTES = 4194304
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class NoRedirects(HTTPRedirectHandler):
|
|
20
|
+
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def endpoint_for(base_url):
|
|
25
|
+
"""Credentials stay on the canonical site or an explicitly chosen loopback."""
|
|
26
|
+
try:
|
|
27
|
+
parsed = urlsplit(base_url)
|
|
28
|
+
canonical = parsed.scheme == 'https' and parsed.hostname == 'classicchess.com' and parsed.port in (None, 443)
|
|
29
|
+
local = parsed.scheme == 'http' and parsed.hostname in ('127.0.0.1', 'localhost', '::1')
|
|
30
|
+
if not (canonical or local) or parsed.username or parsed.password or parsed.query or parsed.fragment or parsed.path not in ('', '/'):
|
|
31
|
+
raise ValueError
|
|
32
|
+
except ValueError:
|
|
33
|
+
raise ValueError('Use https://classicchess.com or an explicit HTTP loopback server.') from None
|
|
34
|
+
return base_url.rstrip('/') + '/mcp/'
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def relay(message, *, endpoint, token, protocol='2025-03-26'):
|
|
38
|
+
body = json.dumps(message, ensure_ascii=False).encode('utf-8')
|
|
39
|
+
if len(body) > MAX_MESSAGE_BYTES:
|
|
40
|
+
raise ValueError('Message exceeds 256 KiB.')
|
|
41
|
+
parameters = message.get('params', {}) if isinstance(message, dict) else {}
|
|
42
|
+
metadata = parameters.get('_meta', {}) if isinstance(parameters, dict) else {}
|
|
43
|
+
version = metadata.get('io.modelcontextprotocol/protocolVersion') if isinstance(metadata, dict) else None
|
|
44
|
+
if version is not None:
|
|
45
|
+
if not isinstance(version, str) or not version.isascii() or len(version) != 10 or not all(c.isdigit() or c == '-' for c in version):
|
|
46
|
+
raise ValueError('Invalid protocol metadata.')
|
|
47
|
+
protocol = version
|
|
48
|
+
headers = {
|
|
49
|
+
'Authorization': f'Bearer {token}', 'Content-Type': 'application/json',
|
|
50
|
+
'Accept': 'application/json, text/event-stream',
|
|
51
|
+
'MCP-Protocol-Version': protocol,
|
|
52
|
+
'User-Agent': 'ClassicChess-MCP/0.1',
|
|
53
|
+
}
|
|
54
|
+
if version is not None:
|
|
55
|
+
method = message.get('method', '')
|
|
56
|
+
if not isinstance(method, str) or not method.isascii() or any(not 33 <= ord(c) <= 126 for c in method):
|
|
57
|
+
raise ValueError('Invalid method.')
|
|
58
|
+
headers['Mcp-Method'] = method
|
|
59
|
+
if method in ('tools/call', 'resources/read', 'prompts/get'):
|
|
60
|
+
name = parameters.get('name', parameters.get('uri', ''))
|
|
61
|
+
if not isinstance(name, str):
|
|
62
|
+
raise ValueError('Invalid tool name.')
|
|
63
|
+
if name.strip() != name or any(not 32 <= ord(c) <= 126 for c in name) or name.startswith('=?base64?'):
|
|
64
|
+
name = '=?base64?' + base64.b64encode(name.encode('utf-8')).decode('ascii') + '?='
|
|
65
|
+
headers['Mcp-Name'] = name
|
|
66
|
+
request = Request(endpoint, data=body, method='POST', headers=headers)
|
|
67
|
+
try:
|
|
68
|
+
with build_opener(NoRedirects()).open(request, timeout=30) as response:
|
|
69
|
+
data = response.read(MAX_RESPONSE_BYTES + 1)
|
|
70
|
+
if len(data) > MAX_RESPONSE_BYTES:
|
|
71
|
+
raise ValueError('Server response exceeds 4 MiB.')
|
|
72
|
+
if response.status == 202:
|
|
73
|
+
return None
|
|
74
|
+
return json.loads(data)
|
|
75
|
+
except HTTPError as exc:
|
|
76
|
+
if exc.code in (400, 404) and exc.headers.get_content_type() == 'application/json':
|
|
77
|
+
try:
|
|
78
|
+
data = exc.read(MAX_RESPONSE_BYTES + 1)
|
|
79
|
+
result = json.loads(data) if len(data) <= MAX_RESPONSE_BYTES else None
|
|
80
|
+
if (isinstance(result, dict) and result.get('jsonrpc') == '2.0'
|
|
81
|
+
and result.get('id') == (message.get('id') if isinstance(message, dict) else None) and isinstance(result.get('error'), dict)
|
|
82
|
+
and type(result['error'].get('code')) is int):
|
|
83
|
+
return result
|
|
84
|
+
except (ValueError, UnicodeDecodeError, RecursionError):
|
|
85
|
+
pass
|
|
86
|
+
# Do not echo headers, credential material, or an untrusted HTML body.
|
|
87
|
+
retry = exc.headers.get('Retry-After', '')
|
|
88
|
+
hint = f' Retry after {retry} seconds.' if retry.isdecimal() else ''
|
|
89
|
+
raise ValueError(f'Server rejected the request (HTTP {exc.code}).{hint}') from None
|
|
90
|
+
except (URLError, TimeoutError, OSError):
|
|
91
|
+
raise ValueError('Cannot reach the Classic Chess MCP server.') from None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def serve(input_stream, output_stream, *, endpoint, token):
|
|
95
|
+
protocol = '2025-03-26'
|
|
96
|
+
while True:
|
|
97
|
+
line = input_stream.readline(MAX_MESSAGE_BYTES + 1)
|
|
98
|
+
if not line:
|
|
99
|
+
return
|
|
100
|
+
if len(line) > MAX_MESSAGE_BYTES:
|
|
101
|
+
# Drain the oversized frame instead of executing its suffix.
|
|
102
|
+
while line and not line.endswith(b'\n'):
|
|
103
|
+
line = input_stream.readline(MAX_MESSAGE_BYTES + 1)
|
|
104
|
+
result = {'jsonrpc': '2.0', 'id': None, 'error': {'code': -32600, 'message': 'Message exceeds 256 KiB.'}}
|
|
105
|
+
else:
|
|
106
|
+
message = None
|
|
107
|
+
try:
|
|
108
|
+
message = json.loads(line)
|
|
109
|
+
result = relay(message, endpoint=endpoint, token=token, protocol=protocol)
|
|
110
|
+
if isinstance(result, dict) and isinstance(result.get('result'), dict):
|
|
111
|
+
protocol = result['result'].get('protocolVersion', protocol)
|
|
112
|
+
except (ValueError, UnicodeDecodeError, RecursionError) as exc:
|
|
113
|
+
identifier = message.get('id') if isinstance(message, dict) else None
|
|
114
|
+
result = {'jsonrpc': '2.0', 'id': identifier, 'error': {'code': -32603, 'message': str(exc) if message is not None else 'Invalid JSON.'}}
|
|
115
|
+
if result is not None:
|
|
116
|
+
output_stream.write(json.dumps(result, ensure_ascii=False) + '\n')
|
|
117
|
+
output_stream.flush()
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def main(argv=None):
|
|
121
|
+
parser = argparse.ArgumentParser(description='Classic Chess stdio MCP transport')
|
|
122
|
+
parser.add_argument('--base-url', default=os.environ.get('CLASSICCHESS_API_BASE_URL', 'https://classicchess.com'))
|
|
123
|
+
args = parser.parse_args(argv)
|
|
124
|
+
token = os.environ.get('CLASSICCHESS_API_TOKEN', '')
|
|
125
|
+
if not token or len(token) > 256 or not token.isascii() or any(char.isspace() for char in token):
|
|
126
|
+
print('Set CLASSICCHESS_API_TOKEN to your personal API token.', file=sys.stderr)
|
|
127
|
+
return 2
|
|
128
|
+
try:
|
|
129
|
+
endpoint = endpoint_for(args.base_url)
|
|
130
|
+
except ValueError as exc:
|
|
131
|
+
print(str(exc), file=sys.stderr)
|
|
132
|
+
return 2
|
|
133
|
+
serve(sys.stdin.buffer, sys.stdout, endpoint=endpoint, token=token)
|
|
134
|
+
return 0
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
if __name__ == '__main__':
|
|
138
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: classicchess-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Classic Chess stdio MCP client for its hosted study-plan server
|
|
5
|
+
Author: Christian Walls
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://classicchess.com/api/
|
|
8
|
+
Project-URL: Documentation, https://classicchess.com/api/docs/integration/#ai-agents
|
|
9
|
+
Project-URL: Download, https://classicchess.com/api/downloads/classicchess-mcp.zip
|
|
10
|
+
Project-URL: Support, https://classicchess.com/support/
|
|
11
|
+
Keywords: chess,mcp,study-plans
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# Classic Chess MCP
|
|
21
|
+
|
|
22
|
+
Connect an assistant to Classic Chess to find games, read PGNs, biographies and
|
|
23
|
+
published annotations, and save study plans with notes and shared progress.
|
|
24
|
+
The hosted MCP URL is `https://classicchess.com/mcp/`.
|
|
25
|
+
|
|
26
|
+
## Release availability
|
|
27
|
+
|
|
28
|
+
Version 0.1.0 is a development preview of the relay. The Python package is
|
|
29
|
+
available on PyPI; the hosted MCP endpoint and browser setup guide are awaiting
|
|
30
|
+
the website deployment. Until then, use a local Classic Chess development
|
|
31
|
+
server with `--base-url http://127.0.0.1:PORT`, replacing `PORT` with its port.
|
|
32
|
+
|
|
33
|
+
## Browser users
|
|
34
|
+
|
|
35
|
+
Claude and ChatGPT browser users connect to the hosted URL and sign in with
|
|
36
|
+
their own Classic Chess account. Follow the
|
|
37
|
+
[browser setup guide](https://classicchess.com/api/docs/integration/#ai-agents)
|
|
38
|
+
for the current menus, account-plan requirements and workspace permissions.
|
|
39
|
+
Choose automatic OAuth client registration in Claude. ChatGPT's available
|
|
40
|
+
read and write actions depend on its plan and workspace permissions.
|
|
41
|
+
|
|
42
|
+
Public searches work without account access. Approve library read and write
|
|
43
|
+
access to save plans and read progress. Revoke the connection in Classic Chess
|
|
44
|
+
[Settings](https://classicchess.com/settings/). A Python installation is needed
|
|
45
|
+
only for the optional stdio relay below.
|
|
46
|
+
|
|
47
|
+
## Claude Code
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
claude mcp add --scope user --transport http classicchess https://classicchess.com/mcp/
|
|
51
|
+
claude mcp login classicchess
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Complete browser sign-in and approve library access. Enable Classic Chess in
|
|
55
|
+
a new session and ask, for example: “Build a five-game rook-endgame study plan
|
|
56
|
+
and save it to my account.”
|
|
57
|
+
|
|
58
|
+
## Codex
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
codex mcp add classicchess --url https://classicchess.com/mcp/
|
|
62
|
+
codex mcp login classicchess --scopes library:read,library:write
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Codex may start browser sign-in when adding the server. The login command also
|
|
66
|
+
lets you connect again later. Plans and progress belong to the account you
|
|
67
|
+
approve, and are shared with the Classic Chess website.
|
|
68
|
+
|
|
69
|
+
## Optional Python stdio relay
|
|
70
|
+
|
|
71
|
+
This MIT-licensed package needs Python 3.10 or later and has no runtime
|
|
72
|
+
dependencies. It relays newline-delimited MCP messages to the same server.
|
|
73
|
+
Install it from [PyPI](https://pypi.org/project/classicchess-mcp/) into a virtual
|
|
74
|
+
environment.
|
|
75
|
+
|
|
76
|
+
On Linux or macOS:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
python3 -m venv classicchess-mcp-env
|
|
80
|
+
classicchess-mcp-env/bin/python -m pip install classicchess-mcp
|
|
81
|
+
classicchess-mcp-env/bin/classicchess-mcp --help
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
On Windows:
|
|
85
|
+
|
|
86
|
+
```powershell
|
|
87
|
+
py -m venv classicchess-mcp-env
|
|
88
|
+
classicchess-mcp-env\Scripts\python -m pip install classicchess-mcp
|
|
89
|
+
classicchess-mcp-env\Scripts\classicchess-mcp --help
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
In a source checkout, install with `python3 -m pip install .` inside your virtual
|
|
93
|
+
environment. The distribution name is `classicchess-mcp`. The
|
|
94
|
+
[website source ZIP](https://classicchess.com/api/downloads/classicchess-mcp.zip)
|
|
95
|
+
will also be available after the website deployment.
|
|
96
|
+
|
|
97
|
+
Create a personal token in [Settings](https://classicchess.com/settings/) with
|
|
98
|
+
`library:read` and `library:write`, then configure your MCP client's private
|
|
99
|
+
environment:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"mcpServers": {
|
|
104
|
+
"classicchess": {
|
|
105
|
+
"command": "FULL_PATH_TO_CLASSICCHESS_MCP",
|
|
106
|
+
"env": {"CLASSICCHESS_API_TOKEN": "YOUR_PERSONAL_TOKEN"}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Replace `FULL_PATH_TO_CLASSICCHESS_MCP` with the absolute path to the executable
|
|
113
|
+
in the virtual environment above (`bin/classicchess-mcp` on Linux/macOS,
|
|
114
|
+
`Scripts\classicchess-mcp.exe` on Windows). Replace `YOUR_PERSONAL_TOKEN` only
|
|
115
|
+
in your client's private configuration. Revoke the token in Settings.
|
|
116
|
+
The relay writes only MCP messages to stdout and never prints credentials,
|
|
117
|
+
follows redirects or sends credentials to another origin.
|
|
118
|
+
|
|
119
|
+
For local development, pass `--base-url http://127.0.0.1:PORT`, replacing `PORT`
|
|
120
|
+
with your development server's port. The canonical HTTPS site and an explicitly
|
|
121
|
+
selected HTTP loopback origin are accepted.
|
|
122
|
+
|
|
123
|
+
## Tools and protocol
|
|
124
|
+
|
|
125
|
+
The nine tools are `search_players`, `search_games`, `get_game_pgn`,
|
|
126
|
+
`get_annotations`, `get_player_bio`, `create_plan`, `update_plan`, `mark_studied`
|
|
127
|
+
and `get_progress`. Plan creation accepts 1–100 ordered game slugs and original
|
|
128
|
+
notes. Reuse a stable idempotency key when retrying the same plan. `update_plan`
|
|
129
|
+
performs one operation per call: metadata, reorder, a note, add a game or
|
|
130
|
+
remove an item. Theme rules and evidence are at `/api/v1/public/themes/`.
|
|
131
|
+
|
|
132
|
+
The server uses stateless Streamable HTTP with JSON responses. It supports
|
|
133
|
+
protocol `2026-07-28` with per-request metadata and bound routing headers, plus
|
|
134
|
+
the initialization flows for `2025-11-25`, `2025-06-18` and `2025-03-26`.
|
|
135
|
+
It publishes OAuth discovery, supports dynamic client registration, requires
|
|
136
|
+
S256 PKCE and accepts native loopback callbacks when the issuer is production
|
|
137
|
+
HTTPS. Clients supporting fixed headers can use personal API tokens instead.
|
|
138
|
+
Source-attributed annotations retain their source and reuse terms.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
classicchess_mcp/__init__.py
|
|
5
|
+
classicchess_mcp/__main__.py
|
|
6
|
+
classicchess_mcp.egg-info/PKG-INFO
|
|
7
|
+
classicchess_mcp.egg-info/SOURCES.txt
|
|
8
|
+
classicchess_mcp.egg-info/dependency_links.txt
|
|
9
|
+
classicchess_mcp.egg-info/entry_points.txt
|
|
10
|
+
classicchess_mcp.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
classicchess_mcp
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "classicchess-mcp"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Classic Chess stdio MCP client for its hosted study-plan server"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{name = "Christian Walls"}]
|
|
14
|
+
keywords = ["chess", "mcp", "study-plans"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
]
|
|
20
|
+
dependencies = []
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://classicchess.com/api/"
|
|
24
|
+
Documentation = "https://classicchess.com/api/docs/integration/#ai-agents"
|
|
25
|
+
Download = "https://classicchess.com/api/downloads/classicchess-mcp.zip"
|
|
26
|
+
Support = "https://classicchess.com/support/"
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
classicchess-mcp = "classicchess_mcp.__main__:main"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools]
|
|
32
|
+
packages = ["classicchess_mcp"]
|