关于 Shelem Scorekeeping
Shelem Scorekeeping 是为波斯吃墩纸牌游戏 Shelem 计分的本地 MCP 服务,适用于由 AI 助手根据对话记录牌局的玩家。它以确定性整数运算处理计分,并通过严格校验拒绝信息缺失或矛盾的牌局。
主要功能
- 01计算每手牌的分数变化
- 02支持 Mak、Sweep 和 Shelem 规则
- 03执行严格输入校验
- 04返回结构化结果和计分说明
- 05无状态运行,不访问网络或数据库
适用场景
- 01根据牌局描述自动更新分数
- 02核对 1000 分规则和胜局判定
- 03通过命令行计算单手牌分数
MCP 相关
该本地 MCP 服务通过 STDIO 向客户端提供 calculate_shelem_score 工具,执行计分并返回结构化结果。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
1收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| bid | integer | 是 | 获胜叫牌:100、105、……、165。请先展开口头缩写("20" 表示 120)。 |
| outcome | any | 否 | "mak":叫牌方恰好取得叫牌分。"sweep":防守方未取得任何分数。可替代分数字段使用,也可与分数字段一致地使用。 |
| contract | any | 否 | 低于 165 时省略。叫牌为 165 时必填:"shelem"、"sare-shelem" 或 "normal"(失败时计分不同)。 |
| hakem_team | any | 否 | Hakem(庄家)所在的队伍:1 或 2。 |
| team1_score | integer | 是 | 第 1 队在本局开始前的累计得分。可以为负数。 |
| team2_score | integer | 是 | 第 2 队在本局开始前的累计得分。可以为负数。 |
| bidding_team | any | 否 | 赢得叫牌的队伍:1 或 2。如果两项都提供,必须与 hakem_team 相同。两者至少需要提供一项。 |
| bid_successful | any | 否 | 可选的交叉验证。成功与否由取得的分数决定;如果此标志与之不符,则拒绝本局。Mak 算作成功。 |
| declarer_points | any | 否 | 叫牌队伍取得的分数:0~165,且必须是 5 的倍数。如果两项都提供,与 defending_points 的总和必须为 165。 |
| defending_points | any | 否 | 防守队伍取得的分数:0~165,且必须是 5 的倍数。 |
详细介绍
Shelem 记分 MCP 服务
一个本地 Model Context Protocol 服务,用于为**Shelem(شلم)**这款波斯吃墩纸牌游戏计分。AI 助手从对话中读取叫牌和结果,此服务使用确定性的整数运算完成每次分数变动。助手无需自行计算。
此服务通过 STDIO 在你的机器上运行。它无法访问网络,没有数据库,也不保存状态。无需托管任何内容。
- PyPI 软件包:
shelem-scorekeeping-mcp - MCP Registry 名称:
io.github.khaleghika/shelem-scorekeeping(Registry 搜索) - 源码:github.com/khaleghika/shelem-mcp
功能
- 一个工具
calculate_shelem_score,具有类型化输入和结构化输出。 - 计分引擎(
shelem_mcp.scoring)独立于 MCP,仅使用 Python 标准库,且从不使用浮点数。 - 严格验证。手牌信息缺失或自相矛盾时,会返回注明字段的错误。服务绝不会猜测分数。
- 根据引擎结果生成每手牌的通俗易懂的
explanation。 - 诊断信息仅写入 stderr。stdout 只承载 MCP 消息。
支持的 Shelem 规则
完整规范(包括源规则中的矛盾及其解决方式)见 docs/scoring-rules.md。同一文档的波斯语版本见 docs/scoring-rules.fa.md。
- 每手牌共分配 165 分。普通叫牌范围为 100 至 165,步进为 5。
- Hakem 是叫牌方。
hakem_team和bidding_team指的是同一队伍。 - **叫牌成功:**Hakem 队取得的分数至少达到叫牌分数,得
+bid分。防守方得其所取得的分数。 - **Mak(مک):**Hakem 队取得的分数恰好等于叫牌分数。这属于叫牌成功,而非失败。
- **全墩:**防守方一分未得,Hakem 队得
+2 × bid分。 - **叫牌失败:**Hakem 队取得的分数低于叫牌分数,得
−bid分。如果防守方取得 85 分或以上,Hakem 队得−2 × bid分。防守方始终得其所取得的分数。 - Shelem / sar-shelem(叫牌 165 分并拿下所有分数):成功时得
+330分;若防守方取得 5–80 分,得−330分;若防守方取得 85 分或以上,得−660分。sar-shelem 的计分方式与 Shelem 相同。 - 普通的 165 分叫牌按普通规则计分,因此叫牌为 165 分时必须注明契约类型。
- **1000 分规则:**手牌开始前已达到 1000 分或以上的防守方,若 Hakem 队叫牌成功,则得 0 分。若叫牌失败,则得其所取得的分数。达到或超过 1000 分的队伍若为 Hakem 队,则按普通规则计分。
- **获胜:**恰好有一支队伍达到 1165 分或以上时,游戏结束。未达到时游戏继续。分数不封顶,允许为负数。没有平局决胜规则,因此服务不会自行编造规则。
安装
软件包名称为 shelem-scorekeeping-mcp,安装的命令也同名。需要 Python 3.10 或更高版本。
使用 uvx(推荐)
uv 会在隔离环境中下载并运行服务,因此无需手动安装任何内容:
uvx shelem-scorekeeping-mcp --version
使用 pip
pip install shelem-scorekeeping-mcp
shelem-scorekeeping-mcp --version
python -m shelem_mcp 也可以启动服务。
从本地检出版本运行(软件包尚未发布到 PyPI 时)
git clone https://github.com/khaleghika/shelem-mcp
cd shelem-mcp
uvx --from . shelem-scorekeeping-mcp --version
不带参数运行时,该命令会通过 STDIO 启动 MCP 服务并等待客户端连接。通常无需自行运行它,MCP 客户端会负责启动。
选项:
--log-level DEBUG|INFO|WARNING|ERROR|CRITICAL设置写入 stderr 的诊断信息级别。默认值为WARNING。环境变量SHELEM_MCP_LOG_LEVEL也可执行相同设置。--version显示版本。
MCP 客户端配置
Claude Desktop
编辑 claude_desktop_config.json。在 macOS 上,文件位于 ~/Library/Application Support/Claude/claude_desktop_config.json。在 Windows 上,文件位于 %APPDATA%\Claude\claude_desktop_config.json。
{
"mcpServers": {
"shelem-scorekeeping": {
"command": "uvx",
"args": ["shelem-scorekeeping-mcp"]
}
}
}
之后重启 Claude Desktop。如果改用 pip 安装,请使用 "command": "shelem-scorekeeping-mcp" 和 "args": []。
Claude Code
claude mcp add shelem-scorekeeping -- uvx shelem-scorekeeping-mcp
添加 --scope user,即可在每个项目中使用。也可以提交项目级别的 .mcp.json:
{
"mcpServers": {
"shelem-scorekeeping": {
"type": "stdio",
"command": "uvx",
"args": ["shelem-scorekeeping-mcp"]
}
}
}
Cursor 和其他客户端
任何可启动 STDIO 服务的客户端都使用相同的 command 和 args。在 Cursor 中,将上面的 Claude Desktop 配置块添加到 ~/.cursor/mcp.json,或项目中的 .cursor/mcp.json。
使用方法
用自己的话告诉助手游戏情况,例如:"双方当前都是 1000。Team 1 是 Hakem,叫牌 20,并且恰好完成(مک)。" 助手会将口述的叫牌“20”扩展为 120,并调用工具:
{
"team1_score": 1000,
"team2_score": 1000,
"hakem_team": 1,
"bidding_team": 1,
"bid": 120,
"outcome": "mak"
}
服务会返回以下结构化结果:
{
"updated_team1_score": 1120,
"updated_team2_score": 1000,
"game_finished": false,
"winner": null,
"score_changes": {
"team1": 120,
"team2": 0
},
"explanation": "Team 1 (Hakem) declared a normal bid of 120. Team 1 captured 120 points and Team 2 captured 45. Team 1 captured exactly its bid (Mak), which is a made bid: +120. Team 2 had 1000 before the hand (1000 or more) and the bid was made, so its 45 captured points are withheld: +0. New totals: Team 1 1120, Team 2 1000. Neither team has reached 1165, so the game continues.",
"hand": {
"bid": 120,
"contract": "normal",
"bidding_team": 1,
"hakem_team": 1,
"defending_team": 2,
"declarer_points": 120,
"defending_points": 45,
"bid_successful": true,
"scoring_rule": "bid_made",
"thousand_point_rule_applied": true,
"scoring_note": null
}
}
服务是无状态的。助手会保留累计总分,并在每一手开始前传入双方的总分。要更正之前的一手,助手会从那一手起重新计算每一手的得分。
工具参考:calculate_shelem_score
输入
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
team1_score | integer | 是 | 本手开始前 Team 1 的总分。可以为负数。 |
team2_score | integer | 是 | 本手开始前 Team 2 的总分。可以为负数。 |
bid | integer | 是 | 100、105、……、165。必须先将口述的简称扩展为完整叫牌(“20”是 120)。 |
hakem_team | 1 或 2 | 以下两项至少提供一项 | Hakem。 |
bidding_team | 1 或 2 | 以下两项至少提供一项 | 赢得叫牌的队伍。如果两项都提供,必须与 hakem_team 相同。 |
outcome | "mak" 或 "sweep" | 以下三项至少提供一项 | mak:Hakem 恰好拿到叫牌分数。sweep:防守方一分未得。 |
defending_points | integer | 以下三项至少提供一项 | 防守方拿到的分数,范围为 0–165,且必须是 5 的倍数。 |
declarer_points | integer | 以下三项至少提供一项 | Hakem 拿到的分数。与 defending_points 相加必须为 165。 |
contract | "normal"、"shelem"、"sare-shelem" | 当叫牌为 165 时必填 | 叫牌低于 165 时省略。 |
bid_successful | boolean | 否 | 仅用于交叉核对。如果与得分不符,本手将被拒绝。 |
“以下两项至少提供一项”和“以下三项至少提供一项”表示必须提供对应分组中的至少一个字段。若提供多个字段,它们的值必须一致。
仅凭 bid_successful 无法计算一手的得分。Hakem 获得的分数取决于防守方是否一分未得或拿到 85 分及以上,而防守方获得的分数就是其拿到的分数。因此,工具还需要已拿到的分数或 outcome。
输出
| 字段 | 类型 | 含义 |
|---|---|---|
updated_team1_score | integer | 本手结束后 Team 1 的总分。 |
updated_team2_score | integer | 本手结束后 Team 2 的总分。 |
game_finished | boolean | 恰好有一支队伍达到 1165 分或以上时为 true。 |
winner | 1、2 或 null | 仅在游戏结束时设置。 |
score_changes | {team1, team2} | 本手中双方各自增加或减少的分数。 |
explanation | string | 对计分情况的英文说明。 |
hand | object | 详细信息:解析后的叫牌、合约、队伍、拿到的分数、bid_successful、scoring_rule、thousand_point_rule_applied、scoring_note。 |
scoring_rule 的值为 sweep_double、bid_made、bid_failed、heavy_failure、shelem_made、shelem_failed 或 shelem_heavy_failure。
错误
输入无效时,会返回一个 isError: true 的 MCP 工具结果,且消息以 (field: <name>) 结尾,例如:
bid must be from 100 through 165 in steps of 5. Abbreviated spoken bids are not accepted. (field: bid)
出错时不会返回得分。任一队伍已达到 1165 分或以上时,工具会拒绝计算本手得分,因为游戏已经结束。
不使用 MCP 来使用计分引擎
from shelem_mcp.scoring import calculate, explain
result = calculate({"team1_score": 0, "team2_score": 0, "bidding_team": 2, "bid": 135, "defending_points": 35})
print(result["team1_score"], result["team2_score"]) # 35 -135
print(explain(result))
python -m shelem_mcp.scoring < hand.json 会在命令行中根据 JSON 计算一手的得分。
开发与测试
uv venv
uv pip install -e ".[dev]"
uv run pytest
测试涵盖以下内容:
tests/test_scoring.py单独测试引擎:规则来源中的每个完整示例、每个边界值(999 和 1000、1160 和 1165、80 和 85 防守分)、Mak、横扫、Shelem、验证、多手牌游戏,以及 1000-1000 Mak 回归测试。tests/test_mcp_tools.py测试 MCP 层:工具发现和架构、在进程内以及通过真实 STDIO 子进程调用(包括经典的initialize握手和 2026-07-28 协议)、原始 JSON-RPC(用于检查 stdout 仅包含协议消息)、根据官方注册表架构验证server.json,以及将本 README 中的示例与实际输出进行比对。
构建发行包:
uv build
uvx twine check dist/*
发布
server.json 使用当前注册表架构:https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json。这是一个基于 STDIO 的本地 PyPI 包,没有远程端点。此文件顶部的 mcp-name 注释是 PyPI 发布检查的所有权证明。
当前注册表指南:
GitHub Actions
推送诸如 v0.1.0 这样的标签时,会运行 .github/workflows/publish-mcp.yml。标签、包版本以及 server.json 中的两个版本字段必须使用相同的版本号。工作流会运行测试、构建并将 shelem-scorekeeping-mcp 发布到 PyPI(使用名为 PYPI_API_TOKEN 的仓库密钥),随后使用 mcp-publisher v1.8.6 发布此仓库的 server.json。运行前会使用其 Sigstore bundle 验证二进制文件,并通过 GitHub OIDC 进行身份验证(login github-oidc)。仓库中不会存储注册表令牌。
在 pypi.org/manage/account/token 创建 PyPI 令牌。项目尚未创建时,使用 整个账户 范围;之后将其添加为 GitHub Actions 密钥 PYPI_API_TOKEN。
git tag v0.1.0
git push origin v0.1.0
确认发布:
curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.khaleghika/shelem-scorekeeping"
手动发布
按照 快速入门 安装 mcp-publisher,先将包发布到 PyPI,然后:
mcp-publisher validate
mcp-publisher login github
mcp-publisher publish
validate 会根据实时注册表验证 server.json,但不会发布它。
许可证
MIT。请参阅 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。