mcpField
S
Shelem Scorekeeping

Shelem Scorekeeping

本地服务
github.com游戏娱乐02 周前更新

关于 Shelem Scorekeeping

Shelem Scorekeeping 是为波斯吃墩纸牌游戏 Shelem 计分的本地 MCP 服务,适用于由 AI 助手根据对话记录牌局的玩家。它以确定性整数运算处理计分,并通过严格校验拒绝信息缺失或矛盾的牌局。

主要功能

  1. 01计算每手牌的分数变化
  2. 02支持 Mak、Sweep 和 Shelem 规则
  3. 03执行严格输入校验
  4. 04返回结构化结果和计分说明
  5. 05无状态运行,不访问网络或数据库

适用场景

  1. 01根据牌局描述自动更新分数
  2. 02核对 1000 分规则和胜局判定
  3. 03通过命令行计算单手牌分数

MCP 相关

该本地 MCP 服务通过 STDIO 向客户端提供 calculate_shelem_score 工具,执行计分并返回结构化结果。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"khaleghika-shelem-scorekeeping": {
"command": "uvx",
"args": [
"shelem-scorekeeping-mcp"
]
}
}
}
启动命令uvx shelem-scorekeeping-mcp

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypishelem-scorekeeping-mcp0.1.0

工具

1

收录的工具列表 · 更新于 2 周前

参数类型必填说明
bidinteger是获胜叫牌:100、105、……、165。请先展开口头缩写("20" 表示 120)。
outcomeany否"mak":叫牌方恰好取得叫牌分。"sweep":防守方未取得任何分数。可替代分数字段使用,也可与分数字段一致地使用。
contractany否低于 165 时省略。叫牌为 165 时必填:"shelem"、"sare-shelem" 或 "normal"(失败时计分不同)。
hakem_teamany否Hakem(庄家)所在的队伍:1 或 2。
team1_scoreinteger是第 1 队在本局开始前的累计得分。可以为负数。
team2_scoreinteger是第 2 队在本局开始前的累计得分。可以为负数。
bidding_teamany否赢得叫牌的队伍:1 或 2。如果两项都提供,必须与 hakem_team 相同。两者至少需要提供一项。
bid_successfulany否可选的交叉验证。成功与否由取得的分数决定;如果此标志与之不符,则拒绝本局。Mak 算作成功。
declarer_pointsany否叫牌队伍取得的分数:0~165,且必须是 5 的倍数。如果两项都提供,与 defending_points 的总和必须为 165。
defending_pointsany否防守队伍取得的分数:0~165,且必须是 5 的倍数。

详细介绍

Shelem 记分 MCP 服务

一个本地 Model Context Protocol 服务,用于为**Shelem(شلم)**这款波斯吃墩纸牌游戏计分。AI 助手从对话中读取叫牌和结果,此服务使用确定性的整数运算完成每次分数变动。助手无需自行计算。

此服务通过 STDIO 在你的机器上运行。它无法访问网络,没有数据库,也不保存状态。无需托管任何内容。

功能

  • 一个工具 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_scoreinteger是本手开始前 Team 1 的总分。可以为负数。
team2_scoreinteger是本手开始前 Team 2 的总分。可以为负数。
bidinteger是100、105、……、165。必须先将口述的简称扩展为完整叫牌(“20”是 120)。
hakem_team1 或 2以下两项至少提供一项Hakem。
bidding_team1 或 2以下两项至少提供一项赢得叫牌的队伍。如果两项都提供,必须与 hakem_team 相同。
outcome"mak" 或 "sweep"以下三项至少提供一项mak:Hakem 恰好拿到叫牌分数。sweep:防守方一分未得。
defending_pointsinteger以下三项至少提供一项防守方拿到的分数,范围为 0–165,且必须是 5 的倍数。
declarer_pointsinteger以下三项至少提供一项Hakem 拿到的分数。与 defending_points 相加必须为 165。
contract"normal"、"shelem"、"sare-shelem"当叫牌为 165 时必填叫牌低于 165 时省略。
bid_successfulboolean否仅用于交叉核对。如果与得分不符,本手将被拒绝。

“以下两项至少提供一项”和“以下三项至少提供一项”表示必须提供对应分组中的至少一个字段。若提供多个字段,它们的值必须一致。

仅凭 bid_successful 无法计算一手的得分。Hakem 获得的分数取决于防守方是否一分未得或拿到 85 分及以上,而防守方获得的分数就是其拿到的分数。因此,工具还需要已拿到的分数或 outcome。

输出
字段类型含义
updated_team1_scoreinteger本手结束后 Team 1 的总分。
updated_team2_scoreinteger本手结束后 Team 2 的总分。
game_finishedboolean恰好有一支队伍达到 1165 分或以上时为 true。
winner1、2 或 null仅在游戏结束时设置。
score_changes{team1, team2}本手中双方各自增加或减少的分数。
explanationstring对计分情况的英文说明。
handobject详细信息:解析后的叫牌、合约、队伍、拿到的分数、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。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。