agentcheckup 0.4.1__tar.gz → 0.4.3__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.
Files changed (54) hide show
  1. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/.github/ISSUE_TEMPLATE/config.yml +2 -2
  2. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/.github/workflows/publish.yml +1 -1
  3. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/CHANGELOG.md +18 -0
  4. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/CONTRIBUTING.md +1 -1
  5. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/LICENSE +1 -1
  6. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/PKG-INFO +20 -10
  7. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/README.md +15 -5
  8. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/README.zh-CN.md +10 -1
  9. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/SECURITY.md +1 -1
  10. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/action.yml +1 -1
  11. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/docs/github-action.md +6 -6
  12. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/pyproject.toml +5 -5
  13. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/conflicts.py +33 -6
  14. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/report.py +3 -3
  15. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/tests/test_conflicts.py +37 -0
  16. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/.gitattributes +0 -0
  17. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  18. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/.github/ISSUE_TEMPLATE/rule_request.yml +0 -0
  19. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/.github/pull_request_template.md +0 -0
  20. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/.github/workflows/action-selftest.yml +0 -0
  21. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/.github/workflows/ci.yml +0 -0
  22. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/.gitignore +0 -0
  23. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/CODE_OF_CONDUCT.md +0 -0
  24. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/Makefile +0 -0
  25. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/conftest.py +0 -0
  26. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/docs/rules.md +0 -0
  27. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/scripts/bootstrap.ps1 +0 -0
  28. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/scripts/bootstrap.sh +0 -0
  29. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/__init__.py +0 -0
  30. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/__main__.py +0 -0
  31. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/cli.py +0 -0
  32. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/discovery.py +0 -0
  33. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/engine.py +0 -0
  34. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/loader.py +0 -0
  35. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/models.py +0 -0
  36. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/rules/__init__.py +0 -0
  37. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/rules/base.py +0 -0
  38. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/rules/config_rules.py +0 -0
  39. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/rules/docs.py +0 -0
  40. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/rules/injection.py +0 -0
  41. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/rules/naming.py +0 -0
  42. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/rules/safety.py +0 -0
  43. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/rules/schema.py +0 -0
  44. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/src/agentcheckup/tokens.py +0 -0
  45. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/tests/fixtures/bad_tools.json +0 -0
  46. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/tests/fixtures/ci_config.json +0 -0
  47. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/tests/fixtures/claude_desktop_config.json +0 -0
  48. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/tests/fixtures/good_tools.json +0 -0
  49. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/tests/fixtures/mock_server.py +0 -0
  50. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/tests/test_cli.py +0 -0
  51. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/tests/test_discovery.py +0 -0
  52. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/tests/test_registry.py +0 -0
  53. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/tests/test_rules.py +0 -0
  54. {agentcheckup-0.4.1 → agentcheckup-0.4.3}/tests/test_scoring.py +0 -0
@@ -1,8 +1,8 @@
1
1
  blank_issues_enabled: false
2
2
  contact_links:
3
3
  - name: Question or idea
4
- url: https://github.com/wanjiaqing/agentcheckup/discussions
4
+ url: https://github.com/jiaqinq/agentcheckup/discussions
5
5
  about: For open-ended discussion, use Discussions instead of an issue.
6
6
  - name: Security vulnerability
7
- url: https://github.com/wanjiaqing/agentcheckup/security/advisories/new
7
+ url: https://github.com/jiaqinq/agentcheckup/security/advisories/new
8
8
  about: Please report security issues privately, not in a public issue.
@@ -7,7 +7,7 @@ name: Publish
7
7
  # publisher"):
8
8
  # https://pypi.org/manage/account/publishing/
9
9
  # PyPI Project Name : agentcheckup
10
- # Owner : wanjiaqing
10
+ # Owner : jiaqinq
11
11
  # Repository name : agentcheckup
12
12
  # Workflow name : publish.yml
13
13
  # Environment : pypi
@@ -9,6 +9,24 @@ new ids, and removing a rule is a breaking change.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.4.2] - 2026-11-07
13
+
14
+ Three claims the tool made that it had not earned. All were found by an external
15
+ review of the README and the report text, not by a bug report.
16
+
17
+ ### Fixed
18
+
19
+ - **`AGT201` no longer asserts what the client does.** The hint said "the client
20
+ keeps one and the other is unreachable". No client was ever tested; the
21
+ behaviour differs between clients and some namespace the tools instead. The
22
+ message now says the call can reach either one and that clients resolve it
23
+ differently, which is true without a test.
24
+ - **README no longer claims "No network calls, ever."** agentcheckup makes none
25
+ of its own, but `audit` starts every server in your config, and those processes
26
+ can do whatever they were written to do. The claim now names that boundary and
27
+ points at `--no-spawn`.
28
+ - **README said 27 config locations; there are 26.** Both languages.
29
+
12
30
  ## [0.4.1] - 2026-11-07
13
31
 
14
32
  Both fixes came out of an external fact-check of the project, not from a bug
@@ -6,7 +6,7 @@ your first one takes about ten minutes.
6
6
  ## Setup
7
7
 
8
8
  ```bash
9
- git clone https://github.com/wanjiaqing/agentcheckup
9
+ git clone https://github.com/jiaqinq/agentcheckup
10
10
  cd agentcheckup
11
11
  python -m venv .venv
12
12
  # Windows: .venv\Scripts\activate
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 wanjiaqing
3
+ Copyright (c) 2026 jiaqinq
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentcheckup
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: Diagnose your agent setup: tool conflicts, risky config, context budget.
5
- Project-URL: Homepage, https://github.com/wanjiaqing/agentcheckup
6
- Project-URL: Issues, https://github.com/wanjiaqing/agentcheckup/issues
7
- Project-URL: Changelog, https://github.com/wanjiaqing/agentcheckup/blob/main/CHANGELOG.md
8
- Author: wanjiaqing
5
+ Project-URL: Homepage, https://github.com/jiaqinq/agentcheckup
6
+ Project-URL: Issues, https://github.com/jiaqinq/agentcheckup/issues
7
+ Project-URL: Changelog, https://github.com/jiaqinq/agentcheckup/blob/main/CHANGELOG.md
8
+ Author: jiaqinq
9
9
  License: MIT
10
10
  License-File: LICENSE
11
11
  Keywords: agents,ai,cli,linter,mcp,model-context-protocol,security
@@ -30,7 +30,7 @@ Description-Content-Type: text/markdown
30
30
 
31
31
  > Diagnose your agent setup before your agent does.
32
32
 
33
- [![CI](https://github.com/wanjiaqing/agentcheckup/actions/workflows/ci.yml/badge.svg)](https://github.com/wanjiaqing/agentcheckup/actions/workflows/ci.yml)
33
+ [![CI](https://github.com/jiaqinq/agentcheckup/actions/workflows/ci.yml/badge.svg)](https://github.com/jiaqinq/agentcheckup/actions/workflows/ci.yml)
34
34
  [![PyPI](https://img.shields.io/pypi/v/agentcheckup.svg)](https://pypi.org/project/agentcheckup/)
35
35
  [![Python](https://img.shields.io/pypi/pyversions/agentcheckup.svg)](https://pypi.org/project/agentcheckup/)
36
36
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
@@ -114,7 +114,7 @@ no network calls. It reads JSON, starts processes, prints text.
114
114
  agentcheckup
115
115
  ```
116
116
 
117
- Finds your configs automatically — 27 known locations across Claude Desktop,
117
+ Finds your configs automatically — 26 known locations across Claude Desktop,
118
118
  Claude Code (including its per-project `mcpServers`), Cursor, VS Code, Cline,
119
119
  Roo Code, Kilo Code, Windsurf, Zed, Codex CLI and Continue, plus project-local
120
120
  `.mcp.json` / `.cursor/mcp.json` / `.vscode/mcp.json` — starts each server,
@@ -149,7 +149,7 @@ The fastest way to keep a manifest from regressing, and the version you pin is
149
149
  the version you run — the action installs from its own checkout.
150
150
 
151
151
  ```yaml
152
- - uses: wanjiaqing/agentcheckup@v0.4.1
152
+ - uses: jiaqinq/agentcheckup@v0.4.3
153
153
  with:
154
154
  config: .mcp.json
155
155
  fail-on: warning
@@ -193,6 +193,11 @@ Each tool pair across servers is scored on two signals:
193
193
  - **name overlap** — normalised token Jaccard (`read_files` ≡ `read_file`)
194
194
  - **description overlap** — token Jaccard over stopword-stripped, plural-stemmed words
195
195
 
196
+ Chinese is tokenised as character bigrams (`读取文件` → `读取`, `取文`, `文件`) rather
197
+ than by a segmenter, because a segmenter is a dependency. It is cruder than a real
198
+ word split, but without it every Chinese description produced an empty token set
199
+ and two Chinese tools could never be compared at all.
200
+
196
201
  A pair is flagged when the descriptions overlap ≥ 65% **and** the names share a
197
202
  head noun, or when the blended score clears 0.55. The shared-noun requirement is
198
203
  what keeps `search_repositories` and `search_documents` apart — without it the
@@ -238,8 +243,9 @@ rule. A report nobody can read is a report nobody acts on.
238
243
  to ignore the output.
239
244
  - **Degrade, don't die.** A server that will not start is one finding, not an
240
245
  aborted run.
241
- - **No network calls, ever.** Your config never leaves the machine. This is the
242
- one thing a commercial scanner structurally cannot promise.
246
+ - **No network calls of its own.** No telemetry, no API, no upload; your config
247
+ never leaves the machine. It does *start* the servers you configured, and those
248
+ can do whatever they were written to do — `--no-spawn` skips that entirely.
243
249
  - **Heuristics are labelled as heuristics.** `info` means "a human should
244
250
  decide". The score is a conversation starter, not a certification.
245
251
 
@@ -250,6 +256,10 @@ rule. A report nobody can read is a report nobody acts on.
250
256
  - Not an MCP protocol validator. It checks quality and safety, not conformance —
251
257
  the official `modelcontextprotocol/conformance` suite does that.
252
258
 
259
+ ## Further reading
260
+
261
+ - [多个 MCP Server 一起使用时,如何检测工具冲突?](https://juejin.cn/post/7704995965740580904) — the design story, in Chinese: why the scope changed from a single-server linter to cross-server conflicts, and how the scoring was rebuilt after it graded the official servers 0/100.
262
+
253
263
  ## Contributing
254
264
 
255
265
  Adding a rule takes about ten minutes and one small file — see
@@ -2,7 +2,7 @@
2
2
 
3
3
  > Diagnose your agent setup before your agent does.
4
4
 
5
- [![CI](https://github.com/wanjiaqing/agentcheckup/actions/workflows/ci.yml/badge.svg)](https://github.com/wanjiaqing/agentcheckup/actions/workflows/ci.yml)
5
+ [![CI](https://github.com/jiaqinq/agentcheckup/actions/workflows/ci.yml/badge.svg)](https://github.com/jiaqinq/agentcheckup/actions/workflows/ci.yml)
6
6
  [![PyPI](https://img.shields.io/pypi/v/agentcheckup.svg)](https://pypi.org/project/agentcheckup/)
7
7
  [![Python](https://img.shields.io/pypi/pyversions/agentcheckup.svg)](https://pypi.org/project/agentcheckup/)
8
8
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
@@ -86,7 +86,7 @@ no network calls. It reads JSON, starts processes, prints text.
86
86
  agentcheckup
87
87
  ```
88
88
 
89
- Finds your configs automatically — 27 known locations across Claude Desktop,
89
+ Finds your configs automatically — 26 known locations across Claude Desktop,
90
90
  Claude Code (including its per-project `mcpServers`), Cursor, VS Code, Cline,
91
91
  Roo Code, Kilo Code, Windsurf, Zed, Codex CLI and Continue, plus project-local
92
92
  `.mcp.json` / `.cursor/mcp.json` / `.vscode/mcp.json` — starts each server,
@@ -121,7 +121,7 @@ The fastest way to keep a manifest from regressing, and the version you pin is
121
121
  the version you run — the action installs from its own checkout.
122
122
 
123
123
  ```yaml
124
- - uses: wanjiaqing/agentcheckup@v0.4.1
124
+ - uses: jiaqinq/agentcheckup@v0.4.3
125
125
  with:
126
126
  config: .mcp.json
127
127
  fail-on: warning
@@ -165,6 +165,11 @@ Each tool pair across servers is scored on two signals:
165
165
  - **name overlap** — normalised token Jaccard (`read_files` ≡ `read_file`)
166
166
  - **description overlap** — token Jaccard over stopword-stripped, plural-stemmed words
167
167
 
168
+ Chinese is tokenised as character bigrams (`读取文件` → `读取`, `取文`, `文件`) rather
169
+ than by a segmenter, because a segmenter is a dependency. It is cruder than a real
170
+ word split, but without it every Chinese description produced an empty token set
171
+ and two Chinese tools could never be compared at all.
172
+
168
173
  A pair is flagged when the descriptions overlap ≥ 65% **and** the names share a
169
174
  head noun, or when the blended score clears 0.55. The shared-noun requirement is
170
175
  what keeps `search_repositories` and `search_documents` apart — without it the
@@ -210,8 +215,9 @@ rule. A report nobody can read is a report nobody acts on.
210
215
  to ignore the output.
211
216
  - **Degrade, don't die.** A server that will not start is one finding, not an
212
217
  aborted run.
213
- - **No network calls, ever.** Your config never leaves the machine. This is the
214
- one thing a commercial scanner structurally cannot promise.
218
+ - **No network calls of its own.** No telemetry, no API, no upload; your config
219
+ never leaves the machine. It does *start* the servers you configured, and those
220
+ can do whatever they were written to do — `--no-spawn` skips that entirely.
215
221
  - **Heuristics are labelled as heuristics.** `info` means "a human should
216
222
  decide". The score is a conversation starter, not a certification.
217
223
 
@@ -222,6 +228,10 @@ rule. A report nobody can read is a report nobody acts on.
222
228
  - Not an MCP protocol validator. It checks quality and safety, not conformance —
223
229
  the official `modelcontextprotocol/conformance` suite does that.
224
230
 
231
+ ## Further reading
232
+
233
+ - [多个 MCP Server 一起使用时,如何检测工具冲突?](https://juejin.cn/post/7704995965740580904) — the design story, in Chinese: why the scope changed from a single-server linter to cross-server conflicts, and how the scoring was rebuilt after it graded the official servers 0/100.
234
+
225
235
  ## Contributing
226
236
 
227
237
  Adding a rule takes about ten minutes and one small file — see
@@ -118,6 +118,10 @@ agentcheckup rules # 列出全部规则
118
118
  - **名字重叠**:归一化后的 token Jaccard(`read_files` ≡ `read_file`)
119
119
  - **描述重叠**:去停用词、粗词干化之后的 token Jaccard
120
120
 
121
+ 中文按**字符二元组**切分(`读取文件` → `读取`、`取文`、`文件`),没有引入分词器——
122
+ 分词器就是依赖。这比真正的分词粗,但没有它的时候中文描述会得到空集合,
123
+ 两个中文工具根本无法比较。
124
+
121
125
  判定条件:描述重叠 ≥ 65% **且** 名字共享中心词,或加权分 ≥ 0.55。
122
126
  共享中心词这个约束是关键——没有它,`search_repositories` 和 `search_documents`
123
127
  会因为都含 "search" 被误报,而**充满噪音的报告比没有报告更糟**。
@@ -155,7 +159,8 @@ agentcheckup rules # 列出全部规则
155
159
  - **规则编号永久不变。** `AGT201` 的含义永远不变。
156
160
  - **每条发现都带修法。** 只说「这不好」的规则,训练用户忽略输出。
157
161
  - **降级而不是崩溃。** 起不来的 server 是一条发现,不是中断。
158
- - **永不联网。** 你的配置不会离开本机。这是商业扫描器结构上给不了的承诺。
162
+ - **自己不联网。** 没有遥测、没有 API、不上传任何东西,配置不会离开本机。
163
+ 但它会**启动你配置的 server**,那些进程会做什么取决于它们自己——`--no-spawn` 可完全跳过。
159
164
  - **启发式要标明是启发式。** `info` 的意思是「人来判断」。分数是讨论起点,不是认证。
160
165
 
161
166
  ## 这不是什么
@@ -164,6 +169,10 @@ agentcheckup rules # 列出全部规则
164
169
  - 不是运行时防护。它不在 agent 会话里运行。
165
170
  - 不是 MCP 协议校验器。协议一致性由官方 `modelcontextprotocol/conformance` 负责。
166
171
 
172
+ ## 相关文章
173
+
174
+ - [多个 MCP Server 一起使用时,如何检测工具冲突?](https://juejin.cn/post/7704995965740580904) —— 设计复盘:为什么从单 server linter 改成跨 server 冲突检测,以及评分把官方 server 打成 0 分之后怎么重做。
175
+
167
176
  ## 贡献
168
177
 
169
178
  加一条规则大约十分钟、一个文件,见 [CONTRIBUTING.md](CONTRIBUTING.md)。
@@ -14,7 +14,7 @@ Nothing leaves your machine.
14
14
  ## Reporting a vulnerability
15
15
 
16
16
  Use GitHub's private advisory flow:
17
- `https://github.com/wanjiaqing/agentcheckup/security/advisories/new`
17
+ `https://github.com/jiaqinq/agentcheckup/security/advisories/new`
18
18
 
19
19
  Please do not open a public issue for a vulnerability. Expect an initial response
20
20
  within 7 days.
@@ -1,6 +1,6 @@
1
1
  name: agentcheckup
2
2
  description: Diagnose your agent setup - cross-server tool conflicts, risky MCP config, context budget.
3
- author: wanjiaqing
3
+ author: jiaqinq
4
4
 
5
5
  branding:
6
6
  icon: activity
@@ -13,7 +13,7 @@ jobs:
13
13
  runs-on: ubuntu-latest
14
14
  steps:
15
15
  - uses: actions/checkout@v4
16
- - uses: wanjiaqing/agentcheckup@main
16
+ - uses: jiaqinq/agentcheckup@main
17
17
  with:
18
18
  config: .mcp.json
19
19
  fail-on: warning
@@ -45,7 +45,7 @@ Outputs are published even when the gate fails, so you can gate on score without
45
45
  parsing the log:
46
46
 
47
47
  ```yaml
48
- - uses: wanjiaqing/agentcheckup@main
48
+ - uses: jiaqinq/agentcheckup@main
49
49
  id: doctor
50
50
  with:
51
51
  config: .mcp.json
@@ -64,7 +64,7 @@ permissions:
64
64
 
65
65
  steps:
66
66
  - uses: actions/checkout@v4
67
- - uses: wanjiaqing/agentcheckup@main
67
+ - uses: jiaqinq/agentcheckup@main
68
68
  with:
69
69
  config: .mcp.json
70
70
  sarif-file: agentcheckup.sarif
@@ -87,7 +87,7 @@ for locally. On a fresh runner that usually finds nothing, and agentcheckup exit
87
87
  For CI, pass the config explicitly:
88
88
 
89
89
  ```yaml
90
- - uses: wanjiaqing/agentcheckup@main
90
+ - uses: jiaqinq/agentcheckup@main
91
91
  with:
92
92
  config: |
93
93
  .mcp.json
@@ -99,7 +99,7 @@ For CI, pass the config explicitly:
99
99
  Spawning every MCP server takes time. If you only need the config checks:
100
100
 
101
101
  ```yaml
102
- - uses: wanjiaqing/agentcheckup@main
102
+ - uses: jiaqinq/agentcheckup@main
103
103
  with:
104
104
  config: .mcp.json
105
105
  args: --no-spawn
@@ -113,5 +113,5 @@ are skipped too. It is fast and useful, but it is not the full check.
113
113
  `@main` tracks the branch. For reproducible CI, pin a tag or a commit SHA:
114
114
 
115
115
  ```yaml
116
- - uses: wanjiaqing/agentcheckup@v0.3.0
116
+ - uses: jiaqinq/agentcheckup@v0.3.0
117
117
  ```
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agentcheckup"
7
- version = "0.4.1"
7
+ version = "0.4.3"
8
8
  description = "Diagnose your agent setup: tool conflicts, risky config, context budget."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  license = { text = "MIT" }
12
- authors = [{ name = "wanjiaqing" }]
12
+ authors = [{ name = "jiaqinq" }]
13
13
  keywords = ["mcp", "model-context-protocol", "agents", "ai", "linter", "security", "cli"]
14
14
  classifiers = [
15
15
  "Development Status :: 3 - Alpha",
@@ -33,9 +33,9 @@ dev = ["pytest>=8", "ruff>=0.6"]
33
33
  agentcheckup = "agentcheckup.cli:main"
34
34
 
35
35
  [project.urls]
36
- Homepage = "https://github.com/wanjiaqing/agentcheckup"
37
- Issues = "https://github.com/wanjiaqing/agentcheckup/issues"
38
- Changelog = "https://github.com/wanjiaqing/agentcheckup/blob/main/CHANGELOG.md"
36
+ Homepage = "https://github.com/jiaqinq/agentcheckup"
37
+ Issues = "https://github.com/jiaqinq/agentcheckup/issues"
38
+ Changelog = "https://github.com/jiaqinq/agentcheckup/blob/main/CHANGELOG.md"
39
39
 
40
40
  [tool.hatch.build.targets.wheel]
41
41
  packages = ["src/agentcheckup"]
@@ -42,6 +42,10 @@ STOPWORDS = frozenset(
42
42
 
43
43
  WORD = re.compile(r"[a-z0-9]+")
44
44
 
45
+ #: CJK ideographs, extension A included. Matched per character because Chinese
46
+ #: has no word delimiters and a real segmenter would be a dependency.
47
+ CJK = re.compile(r"[\u3400-\u4dbf\u4e00-\u9fff]+")
48
+
45
49
 
46
50
  @dataclass(frozen=True)
47
51
  class ToolRef:
@@ -94,13 +98,33 @@ def _stem(word: str) -> str:
94
98
  return word
95
99
 
96
100
 
101
+ def _cjk_tokens(text: str) -> set[str]:
102
+ """Character bigrams over every CJK run.
103
+
104
+ Chinese has no spaces, so ``WORD`` returns nothing and two Chinese tools
105
+ would look like two empty sets — silently, which is the worst outcome. A real
106
+ segmenter is a dependency; character bigrams are the standard cheap
107
+ substitute: 读取文件 -> 读取, 取文, 文件.
108
+
109
+ A single-character run is kept whole, so 读 still matches 读.
110
+ """
111
+ out: set[str] = set()
112
+ for run in CJK.findall(text):
113
+ if len(run) == 1:
114
+ out.add(run)
115
+ else:
116
+ out.update(run[i : i + 2] for i in range(len(run) - 1))
117
+ return out
118
+
119
+
97
120
  def tokens(text: str) -> set[str]:
98
- """Lowercase word set, stopwords removed, crude plural stemming."""
99
- return {
121
+ """Lowercase word set, stopwords removed, crude plural stemming, plus CJK."""
122
+ latin = {
100
123
  _stem(w)
101
124
  for w in WORD.findall(text.lower())
102
125
  if w not in STOPWORDS and len(w) > 1
103
126
  }
127
+ return latin | _cjk_tokens(text)
104
128
 
105
129
 
106
130
  def jaccard(a: set[str], b: set[str]) -> float:
@@ -111,7 +135,8 @@ def jaccard(a: set[str], b: set[str]) -> float:
111
135
 
112
136
 
113
137
  def name_tokens(name: str) -> set[str]:
114
- return {_stem(w) for w in WORD.findall(name.lower()) if len(w) > 1}
138
+ latin = {_stem(w) for w in WORD.findall(name.lower()) if len(w) > 1}
139
+ return latin | _cjk_tokens(name)
115
140
 
116
141
 
117
142
  def similarity(a: ToolRef, b: ToolRef) -> float:
@@ -187,7 +212,8 @@ def detect_conflicts(
187
212
  name_a = a.tool.name.lower()
188
213
  name_b = b.tool.name.lower()
189
214
 
190
- # Hard collision: identical names, different servers. One is unreachable.
215
+ # Hard collision: identical names, different servers. What the client
216
+ # does about it varies, so the message must not promise a specific outcome.
191
217
  if name_a == name_b:
192
218
  conflicts.append(
193
219
  Conflict(
@@ -201,8 +227,9 @@ def detect_conflicts(
201
227
  f"'{a.server}' and '{b.server}'."
202
228
  ),
203
229
  hint=(
204
- "Hard collision: the client keeps one and the other is "
205
- "unreachable. Rename one, or use a gateway that prefixes."
230
+ "Two servers expose the same name and clients resolve that "
231
+ "differently, so the same call can reach either one. Rename "
232
+ "one, or use a gateway that prefixes."
206
233
  ),
207
234
  )
208
235
  )
@@ -10,7 +10,7 @@ from . import rules as _rules
10
10
  from .engine import Report
11
11
  from .models import Finding, Severity
12
12
 
13
- __version__ = "0.4.1"
13
+ __version__ = "0.4.3"
14
14
 
15
15
  SARIF_LEVEL = {
16
16
  Severity.ERROR: "error",
@@ -195,7 +195,7 @@ def render_text(report: Report, verbose: bool = False) -> str:
195
195
  lines.extend(_nudge_lines(nudges))
196
196
 
197
197
  lines.append("")
198
- lines.append(" Rule docs: https://github.com/wanjiaqing/agentcheckup#rules")
198
+ lines.append(" Rule docs: https://github.com/jiaqinq/agentcheckup#rules")
199
199
  return "\n".join(lines)
200
200
 
201
201
 
@@ -246,7 +246,7 @@ def render_sarif(report: Report) -> str:
246
246
  "driver": {
247
247
  "name": "agentcheckup",
248
248
  "version": __version__,
249
- "informationUri": "https://github.com/wanjiaqing/agentcheckup",
249
+ "informationUri": "https://github.com/jiaqinq/agentcheckup",
250
250
  "rules": _rule_metadata(),
251
251
  }
252
252
  },
@@ -128,3 +128,40 @@ def test_audit_tokens_sum_across_servers():
128
128
  tools = {"a": [tool("alpha_tool")], "b": [tool("beta_tool")]}
129
129
  report = lint_audit(servers, tools)
130
130
  assert report.tokens > 0
131
+
132
+
133
+ # --------------------------------------------------------------------------- #
134
+ # CJK support
135
+ # --------------------------------------------------------------------------- #
136
+
137
+
138
+ def test_cjk_runs_become_character_bigrams():
139
+ assert tokens("读取文件") == {"读取", "取文", "文件"}
140
+
141
+
142
+ def test_single_cjk_character_is_kept_whole():
143
+ assert tokens("读") == {"读"}
144
+
145
+
146
+ def test_cjk_and_latin_mix():
147
+ got = tokens("read 文件")
148
+ assert "read" in got
149
+ assert "文件" in got
150
+
151
+
152
+ def test_chinese_near_duplicates_are_flagged():
153
+ """Before CJK support this pair produced two empty token sets and no finding."""
154
+ a = ref("文件服务", "读取文件", "读取指定路径的文件内容并返回文本")
155
+ b = ref("文档服务", "读取文档", "读取指定路径的文件内容并返回字符串")
156
+ assert "AGT203" in {c.rule for c in detect_conflicts([a, b])}
157
+
158
+
159
+ def test_chinese_unrelated_tools_are_not_flagged():
160
+ a = ref("文件服务", "读取文件", "读取指定路径的文件内容并返回文本")
161
+ b = ref("文件服务", "删除文件", "删除指定路径的文件")
162
+ assert detect_conflicts([a, b]) == []
163
+
164
+
165
+ def test_english_scores_are_unchanged_by_cjk_support():
166
+ """The CJK path must not touch Latin tokenisation."""
167
+ assert tokens("Read a file") == {"read", "file"}
File without changes
File without changes
File without changes
File without changes