zetesis 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.
- zetesis-0.1.0/LICENSE +21 -0
- zetesis-0.1.0/PKG-INFO +84 -0
- zetesis-0.1.0/PUBLISH.md +53 -0
- zetesis-0.1.0/README.md +62 -0
- zetesis-0.1.0/REGISTRY.md +43 -0
- zetesis-0.1.0/pyproject.toml +35 -0
- zetesis-0.1.0/server.json +26 -0
- zetesis-0.1.0/src/zetesis/__init__.py +1 -0
- zetesis-0.1.0/src/zetesis/client.py +167 -0
zetesis-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Reut Avidan
|
|
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.
|
zetesis-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zetesis
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP client for Zetesis: scientific due diligence on a claim, evaluated by a hosted engine and sealed in a verifiable attestation.
|
|
5
|
+
Project-URL: Homepage, https://api.zetesis.science
|
|
6
|
+
Project-URL: Signup, https://api.zetesis.science/signup
|
|
7
|
+
Author-email: Reut Avidan <reutavidan@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: biotech,claim-verification,due-diligence,evaluation,mcp,research,science
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: mcp>=1.2.0
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# Zetesis
|
|
24
|
+
|
|
25
|
+
Scientific due diligence on a claim, from inside Claude, Copilot, or any MCP host.
|
|
26
|
+
|
|
27
|
+
Zetesis takes a high-dimensional scientific or technical claim and grades it
|
|
28
|
+
dimension by dimension against public literature, clinical trials, and filings.
|
|
29
|
+
It returns an overall reading, cites every source by id, and, for a full
|
|
30
|
+
evaluation, translates the science into what a commitment to the claim turns on
|
|
31
|
+
and seals the result in an attestation anyone can re-check.
|
|
32
|
+
|
|
33
|
+
This package is a thin MCP client. It holds no keys and runs no model. Every call
|
|
34
|
+
is proxied to the hosted engine at <https://api.zetesis.science> with your token.
|
|
35
|
+
|
|
36
|
+
## Get a token
|
|
37
|
+
|
|
38
|
+
Free, no card, at <https://api.zetesis.science/signup>. The free plan gives you a
|
|
39
|
+
handful of screening evaluations each month.
|
|
40
|
+
|
|
41
|
+
## Connect it
|
|
42
|
+
|
|
43
|
+
Claude Code:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
claude mcp add zetesis --env ZETESIS_TOKEN=zk_... -- uvx --from zetesis zetesis-client
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Claude Desktop (`claude_desktop_config.json`):
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"mcpServers": {
|
|
54
|
+
"zetesis": {
|
|
55
|
+
"command": "uvx",
|
|
56
|
+
"args": ["--from", "zetesis", "zetesis-client"],
|
|
57
|
+
"env": { "ZETESIS_TOKEN": "zk_..." }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Tools
|
|
64
|
+
|
|
65
|
+
- **evaluate_claim** — grade a claim. `depth="screen"` returns the verdict directly
|
|
66
|
+
(free tier); `depth="deep"` runs the full signed dossier in the background.
|
|
67
|
+
`mode="research"` is science only; `mode="diligence"` adds the capital reading.
|
|
68
|
+
- **check_evaluation** — get the verdict and the rendered dossier link for a deep run.
|
|
69
|
+
- **verify_attestation** — re-check any Zetesis attestation. No account needed.
|
|
70
|
+
- **account_status** — your plan and remaining evaluations or credits.
|
|
71
|
+
|
|
72
|
+
## How it grades
|
|
73
|
+
|
|
74
|
+
It grades only what the evidence supports, cites sources by hard id (PMID, DOI,
|
|
75
|
+
NCT, NIH grant, SEC filing), and flags gaps rather than filling them. It never
|
|
76
|
+
invents citations. Claim text is not logged; only metadata (the routed domain,
|
|
77
|
+
depth, counts) is kept.
|
|
78
|
+
|
|
79
|
+
## Configuration
|
|
80
|
+
|
|
81
|
+
- `ZETESIS_TOKEN` — your token (required for evaluations; verify works without one)
|
|
82
|
+
- `ZETESIS_API` — override the API base (default `https://api.zetesis.science`)
|
|
83
|
+
|
|
84
|
+
MIT licensed. The hosted engine is a separate service.
|
zetesis-0.1.0/PUBLISH.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Publish `zetesis` to PyPI
|
|
2
|
+
|
|
3
|
+
The package is built and validated in `dist/`. Publishing is your action because
|
|
4
|
+
it needs your PyPI account, and it is permanent (a version number can never be
|
|
5
|
+
reused or reclaimed).
|
|
6
|
+
|
|
7
|
+
## 1. One-time: PyPI account + token
|
|
8
|
+
|
|
9
|
+
- Create an account at <https://pypi.org> (verify the email).
|
|
10
|
+
- Account settings → API tokens → **Add API token**. Scope it to "Entire account"
|
|
11
|
+
for the first upload (you can narrow it to the `zetesis` project afterward).
|
|
12
|
+
- Copy the token (starts with `pypi-`). It is shown once.
|
|
13
|
+
|
|
14
|
+
## 2. Publish
|
|
15
|
+
|
|
16
|
+
From this folder:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
cd /Users/reutavidan/zetesis/01-current/zetesis-pypi
|
|
20
|
+
UV_PUBLISH_TOKEN=pypi-XXXX uv publish
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
(Equivalent with twine: `uvx twine upload -u __token__ -p pypi-XXXX dist/*`.)
|
|
24
|
+
|
|
25
|
+
## 3. Verify it is live
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
uvx --from zetesis zetesis-client # should start (Ctrl+C to stop); confirms install works
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Then the marketplace / user config just uses the published name:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
claude mcp add zetesis --env ZETESIS_TOKEN=zk_... -- uvx --from zetesis zetesis-client
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Optional: dry run on TestPyPI first
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
UV_PUBLISH_TOKEN=pypi-TESTTOKEN uv publish --publish-url https://test.pypi.org/legacy/
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Future releases
|
|
44
|
+
|
|
45
|
+
Bump `version` in `pyproject.toml`, then `uv build` and `uv publish` again. You
|
|
46
|
+
cannot overwrite 0.1.0; the next upload must be 0.1.1 or higher.
|
|
47
|
+
|
|
48
|
+
## After publishing: list on the marketplaces
|
|
49
|
+
|
|
50
|
+
- **Anthropic MCP directory**: submit the server (name, description, the `uvx --from
|
|
51
|
+
zetesis zetesis-client` command, the tool list). Goes through a review.
|
|
52
|
+
- **Microsoft Copilot store**: submit as a connector.
|
|
53
|
+
Two named reference users help both, since neither store features a zero-usage server.
|
zetesis-0.1.0/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Zetesis
|
|
2
|
+
|
|
3
|
+
Scientific due diligence on a claim, from inside Claude, Copilot, or any MCP host.
|
|
4
|
+
|
|
5
|
+
Zetesis takes a high-dimensional scientific or technical claim and grades it
|
|
6
|
+
dimension by dimension against public literature, clinical trials, and filings.
|
|
7
|
+
It returns an overall reading, cites every source by id, and, for a full
|
|
8
|
+
evaluation, translates the science into what a commitment to the claim turns on
|
|
9
|
+
and seals the result in an attestation anyone can re-check.
|
|
10
|
+
|
|
11
|
+
This package is a thin MCP client. It holds no keys and runs no model. Every call
|
|
12
|
+
is proxied to the hosted engine at <https://api.zetesis.science> with your token.
|
|
13
|
+
|
|
14
|
+
## Get a token
|
|
15
|
+
|
|
16
|
+
Free, no card, at <https://api.zetesis.science/signup>. The free plan gives you a
|
|
17
|
+
handful of screening evaluations each month.
|
|
18
|
+
|
|
19
|
+
## Connect it
|
|
20
|
+
|
|
21
|
+
Claude Code:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
claude mcp add zetesis --env ZETESIS_TOKEN=zk_... -- uvx --from zetesis zetesis-client
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Claude Desktop (`claude_desktop_config.json`):
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"zetesis": {
|
|
33
|
+
"command": "uvx",
|
|
34
|
+
"args": ["--from", "zetesis", "zetesis-client"],
|
|
35
|
+
"env": { "ZETESIS_TOKEN": "zk_..." }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Tools
|
|
42
|
+
|
|
43
|
+
- **evaluate_claim** — grade a claim. `depth="screen"` returns the verdict directly
|
|
44
|
+
(free tier); `depth="deep"` runs the full signed dossier in the background.
|
|
45
|
+
`mode="research"` is science only; `mode="diligence"` adds the capital reading.
|
|
46
|
+
- **check_evaluation** — get the verdict and the rendered dossier link for a deep run.
|
|
47
|
+
- **verify_attestation** — re-check any Zetesis attestation. No account needed.
|
|
48
|
+
- **account_status** — your plan and remaining evaluations or credits.
|
|
49
|
+
|
|
50
|
+
## How it grades
|
|
51
|
+
|
|
52
|
+
It grades only what the evidence supports, cites sources by hard id (PMID, DOI,
|
|
53
|
+
NCT, NIH grant, SEC filing), and flags gaps rather than filling them. It never
|
|
54
|
+
invents citations. Claim text is not logged; only metadata (the routed domain,
|
|
55
|
+
depth, counts) is kept.
|
|
56
|
+
|
|
57
|
+
## Configuration
|
|
58
|
+
|
|
59
|
+
- `ZETESIS_TOKEN` — your token (required for evaluations; verify works without one)
|
|
60
|
+
- `ZETESIS_API` — override the API base (default `https://api.zetesis.science`)
|
|
61
|
+
|
|
62
|
+
MIT licensed. The hosted engine is a separate service.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Listing Zetesis on the MCP registry and the Claude Connectors Directory
|
|
2
|
+
|
|
3
|
+
Do this after publishing `zetesis` to PyPI (see PUBLISH.md).
|
|
4
|
+
|
|
5
|
+
## 1. Official MCP Registry (the canonical feed)
|
|
6
|
+
|
|
7
|
+
The registry is the hub; downstream directories (Smithery, Glama, MCP.so, PulseMCP)
|
|
8
|
+
pull from it. `server.json` is ready in this folder.
|
|
9
|
+
|
|
10
|
+
- **Namespace + verification.** `server.json` uses `science.zetesis/zetesis`, which
|
|
11
|
+
requires proving you control zetesis.science (a DNS TXT record or a file the
|
|
12
|
+
registry publisher tells you to serve). Alternatively switch the name to
|
|
13
|
+
`io.github.reutavidan/zetesis` and verify with your GitHub account, which is
|
|
14
|
+
quicker.
|
|
15
|
+
- **Publish.** Install the registry publisher CLI (`mcp-publisher`), authenticate
|
|
16
|
+
for your namespace, and run it against `server.json`. It validates the file
|
|
17
|
+
against the current schema and pushes the listing. Follow the current steps at
|
|
18
|
+
<https://modelcontextprotocol.io/registry/about> (the schema and CLI evolve, so
|
|
19
|
+
validate against the live schema when you submit).
|
|
20
|
+
|
|
21
|
+
## 2. Claude Connectors Directory (to be inside Claude)
|
|
22
|
+
|
|
23
|
+
Separate from the registry, and reviewed by Anthropic. This listing is the credential
|
|
24
|
+
and is what lets Claude surface Zetesis to users automatically.
|
|
25
|
+
|
|
26
|
+
Two things the review checks, both already handled:
|
|
27
|
+
- **Read-only tool annotations.** All four tools are annotated `readOnlyHint: true`
|
|
28
|
+
in the client, so nothing looks destructive.
|
|
29
|
+
- **A public privacy policy.** Live at <https://api.zetesis.science/privacy>. A missing
|
|
30
|
+
or incomplete policy is an automatic rejection.
|
|
31
|
+
|
|
32
|
+
Submit through the Anthropic connectors submission flow with: the name, the discovery
|
|
33
|
+
description, the `uvx --from zetesis zetesis-client` command (env `ZETESIS_TOKEN`
|
|
34
|
+
optional, only for the full dossier), the privacy policy URL, and the tool list.
|
|
35
|
+
|
|
36
|
+
## 3. Discovery is earned, not bought
|
|
37
|
+
|
|
38
|
+
Ranking in the Connectors Directory is by adoption (active users, tool calls), shown
|
|
39
|
+
in a developer dashboard, with no paid placement. Claude surfaces a connector by
|
|
40
|
+
matching the user's intent against tool **names, descriptions, and parameter names**,
|
|
41
|
+
so the descriptions in `client.py` are the discovery surface. Own the phrasings you
|
|
42
|
+
want to win ("evaluate a scientific claim", "how rigorous is this study", "audit this
|
|
43
|
+
biotech or AI claim") and the free screen becomes your spontaneous top-of-funnel.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "zetesis"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "MCP client for Zetesis: scientific due diligence on a claim, evaluated by a hosted engine and sealed in a verifiable attestation."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
authors = [{ name = "Reut Avidan", email = "reutavidan@gmail.com" }]
|
|
9
|
+
keywords = ["mcp", "science", "due-diligence", "evaluation", "claim-verification", "biotech", "research"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 4 - Beta",
|
|
12
|
+
"Intended Audience :: Science/Research",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.10",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Topic :: Scientific/Engineering",
|
|
19
|
+
]
|
|
20
|
+
dependencies = ["mcp>=1.2.0"]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://api.zetesis.science"
|
|
24
|
+
Signup = "https://api.zetesis.science/signup"
|
|
25
|
+
|
|
26
|
+
[project.scripts]
|
|
27
|
+
zetesis-client = "zetesis.client:main"
|
|
28
|
+
zetesis = "zetesis.client:main"
|
|
29
|
+
|
|
30
|
+
[build-system]
|
|
31
|
+
requires = ["hatchling"]
|
|
32
|
+
build-backend = "hatchling.build"
|
|
33
|
+
|
|
34
|
+
[tool.hatch.build.targets.wheel]
|
|
35
|
+
packages = ["src/zetesis"]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
|
|
3
|
+
"name": "science.zetesis/zetesis",
|
|
4
|
+
"description": "Scientific due diligence on a claim. Evaluate how rigorously a scientific, biomedical, clinical, or AI claim was derived, and how well public literature, clinical trials, and filings support it, with a signed, independently re-checkable attestation. Free screen; gated full dossier.",
|
|
5
|
+
"status": "active",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"websiteUrl": "https://api.zetesis.science",
|
|
8
|
+
"packages": [
|
|
9
|
+
{
|
|
10
|
+
"registryType": "pypi",
|
|
11
|
+
"registryBaseUrl": "https://pypi.org",
|
|
12
|
+
"identifier": "zetesis",
|
|
13
|
+
"version": "0.1.0",
|
|
14
|
+
"transport": { "type": "stdio" },
|
|
15
|
+
"runtimeHint": "uvx",
|
|
16
|
+
"environmentVariables": [
|
|
17
|
+
{
|
|
18
|
+
"name": "ZETESIS_TOKEN",
|
|
19
|
+
"description": "Zetesis access token. Only needed for a full attested dossier; the free screen needs none. Request access at https://api.zetesis.science/request-access.",
|
|
20
|
+
"isRequired": false,
|
|
21
|
+
"isSecret": true
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""Thin-client MCP server. Proxies to the hosted Zetesis API.
|
|
2
|
+
|
|
3
|
+
This is the server that lists on the Official MCP Registry and the Claude
|
|
4
|
+
Connectors Directory. It holds no engine, no Anthropic key, and no signing key:
|
|
5
|
+
the user sets ZETESIS_TOKEN (only for the full dossier) and every call goes to
|
|
6
|
+
https://api.zetesis.science. If MCP is superseded, this file is rewritten and the
|
|
7
|
+
hosted engine is untouched.
|
|
8
|
+
|
|
9
|
+
Tool descriptions are written for discovery: the model reads them to decide when
|
|
10
|
+
to reach for Zetesis, so they name the intents this tool should win.
|
|
11
|
+
|
|
12
|
+
Config:
|
|
13
|
+
ZETESIS_TOKEN the user's token (only for a full dossier; the free screen needs none)
|
|
14
|
+
ZETESIS_API override the API base (default https://api.zetesis.science)
|
|
15
|
+
"""
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import urllib.error
|
|
19
|
+
import urllib.request
|
|
20
|
+
|
|
21
|
+
from mcp.server.fastmcp import FastMCP
|
|
22
|
+
|
|
23
|
+
API = os.environ.get("ZETESIS_API", "https://api.zetesis.science").rstrip("/")
|
|
24
|
+
TOKEN = os.environ.get("ZETESIS_TOKEN", "")
|
|
25
|
+
|
|
26
|
+
mcp = FastMCP("zetesis")
|
|
27
|
+
|
|
28
|
+
# every tool here only reads / requests an evaluation; nothing mutates the user's
|
|
29
|
+
# environment. openWorld because each call reaches an external service.
|
|
30
|
+
_READ = {"readOnlyHint": True, "destructiveHint": False, "openWorldHint": True}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@mcp.tool(annotations={**_READ, "title": "Evaluate a scientific or AI claim"})
|
|
34
|
+
def evaluate_claim(claim: str, context: str = "", documents: str = "", depth: str = "screen", mode: str = "research") -> str:
|
|
35
|
+
"""Evaluate how rigorously a scientific, biomedical, clinical, or AI/ML claim was derived, and how well the evidence supports it.
|
|
36
|
+
|
|
37
|
+
Use this whenever someone wants to assess, screen, sanity-check, or do due
|
|
38
|
+
diligence on a research claim, a study, a paper, an abstract, a preprint, a
|
|
39
|
+
grant or a pitch: whether the methodology is sound, whether the data was
|
|
40
|
+
harmonized and controlled properly, whether the statistics hold, whether the
|
|
41
|
+
result reproduces, and how well the public literature, clinical trials, and
|
|
42
|
+
filings back it. It grades each evidence dimension and returns an overall
|
|
43
|
+
reading with sources cited by hard id (PMID, DOI, NCT, NIH grant, SEC filing).
|
|
44
|
+
Works for drug, omics, target-validation, diagnostic, and AI-model claims.
|
|
45
|
+
|
|
46
|
+
A screen is free and needs no token. A full attested dossier (deep) is the
|
|
47
|
+
signed, independently re-checkable audit and needs access.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
claim: the claim to evaluate, in one or two sentences.
|
|
51
|
+
context: optional background (stage, field, the decision at hand).
|
|
52
|
+
documents: optional source text (a deck, abstract, or paper).
|
|
53
|
+
depth: "screen" for a fast, free pass (no token needed), "deep" for the full
|
|
54
|
+
attested dossier (needs access).
|
|
55
|
+
mode: "research" (science only) or "diligence" (adds the capital reading); deep only.
|
|
56
|
+
"""
|
|
57
|
+
if depth == "screen":
|
|
58
|
+
status, d = _call("POST", "/screen",
|
|
59
|
+
{"claim": claim, "context": context, "documents": documents}, auth=False)
|
|
60
|
+
if status == 429:
|
|
61
|
+
return d.get("detail", "Free screens are used up for now.")
|
|
62
|
+
if status == 0:
|
|
63
|
+
return d.get("detail", "network error")
|
|
64
|
+
if status >= 400:
|
|
65
|
+
return f"Error: {d.get('detail', status)}"
|
|
66
|
+
return d.get("summary", "(no summary returned)")
|
|
67
|
+
|
|
68
|
+
if not TOKEN:
|
|
69
|
+
return f"A full attested dossier needs access. Request it at {API}/request-access."
|
|
70
|
+
status, d = _call("POST", "/evaluate",
|
|
71
|
+
{"claim": claim, "context": context, "documents": documents, "depth": "deep", "mode": mode})
|
|
72
|
+
if status == 401:
|
|
73
|
+
return "Your token was rejected. Check ZETESIS_TOKEN."
|
|
74
|
+
if status == 402:
|
|
75
|
+
return f"{d.get('detail', 'This needs a paid plan.')} Manage your plan at {API}."
|
|
76
|
+
if status == 0:
|
|
77
|
+
return d.get("detail", "network error")
|
|
78
|
+
if status >= 400:
|
|
79
|
+
return f"Error: {d.get('detail', status)}"
|
|
80
|
+
jid = d.get("job_id")
|
|
81
|
+
return (f"Full evaluation started (job {jid}). It runs in the background.\n"
|
|
82
|
+
f"Call check_evaluation with job_id \"{jid}\" for the result, "
|
|
83
|
+
f"or open the dossier at {API}/d/{jid} once it is ready.")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@mcp.tool(annotations={**_READ, "title": "Get a full dossier result"})
|
|
87
|
+
def check_evaluation(job_id: str) -> str:
|
|
88
|
+
"""Get the result of a full (deep) Zetesis dossier started earlier. Returns the
|
|
89
|
+
reading and the rendered dossier link once it is ready.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
job_id: the id returned by evaluate_claim for a deep evaluation.
|
|
93
|
+
"""
|
|
94
|
+
status, d = _call("GET", f"/jobs/{job_id}")
|
|
95
|
+
if status >= 400:
|
|
96
|
+
return f"Error: {d.get('detail', status)}"
|
|
97
|
+
st = d.get("status")
|
|
98
|
+
if st == "pending":
|
|
99
|
+
return "Still running. Check again in a moment."
|
|
100
|
+
if st == "error":
|
|
101
|
+
return f"The evaluation failed: {d.get('error')}"
|
|
102
|
+
return d.get("summary", "(no summary)") + f"\n\nRendered dossier: {API}{d.get('dossier_url', '')}"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@mcp.tool(annotations={**_READ, "title": "Verify a Zetesis attestation"})
|
|
106
|
+
def verify_attestation(attestation_json: str) -> str:
|
|
107
|
+
"""Verify a Zetesis attestation, confirming an evaluation's claim, evidence, and
|
|
108
|
+
conclusion have not been altered since it was signed. Use when someone has a
|
|
109
|
+
Zetesis dossier or attestation and wants to independently re-check it. No
|
|
110
|
+
account needed.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
attestation_json: the full attestation object as JSON text.
|
|
114
|
+
"""
|
|
115
|
+
try:
|
|
116
|
+
att = json.loads(attestation_json)
|
|
117
|
+
except json.JSONDecodeError as e:
|
|
118
|
+
return f"Not valid JSON: {e}"
|
|
119
|
+
status, d = _call("POST", "/verify", att, auth=False)
|
|
120
|
+
if status >= 400:
|
|
121
|
+
return f"Error: {d.get('detail', status)}"
|
|
122
|
+
if not d.get("valid"):
|
|
123
|
+
return f"INVALID: {d.get('reason', 'verification failed')}"
|
|
124
|
+
v = d.get("verdict", {})
|
|
125
|
+
return (f"VALID. Attestation {str(d.get('id', ''))[:16]} signed at {d.get('created')} "
|
|
126
|
+
f"using {d.get('model')}. Overall reading: {v.get('overall')}. "
|
|
127
|
+
f"{d.get('evidence_count')} public sources pinned.")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@mcp.tool(annotations={**_READ, "title": "Zetesis account status"})
|
|
131
|
+
def account_status() -> str:
|
|
132
|
+
"""Show the caller's Zetesis plan and remaining free evaluations or dossier credits."""
|
|
133
|
+
if not TOKEN:
|
|
134
|
+
return f"No ZETESIS_TOKEN set. Request access at {API}/request-access."
|
|
135
|
+
status, d = _call("GET", "/me")
|
|
136
|
+
if status >= 400:
|
|
137
|
+
return f"Error: {d.get('detail', status)}"
|
|
138
|
+
cap = d.get("screen_cap")
|
|
139
|
+
cap = "unlimited" if cap is None else cap
|
|
140
|
+
return (f"Plan: {d.get('plan')}. Screens this month: {d.get('screens_used')}/{cap}. "
|
|
141
|
+
f"Dossier credits: {d.get('dossier_balance')}.")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _call(method, path, body=None, auth=True):
|
|
145
|
+
headers = {"Content-Type": "application/json"}
|
|
146
|
+
if auth and TOKEN:
|
|
147
|
+
headers["Authorization"] = f"Bearer {TOKEN}"
|
|
148
|
+
data = json.dumps(body).encode() if body is not None else None
|
|
149
|
+
req = urllib.request.Request(API + path, data=data, headers=headers, method=method)
|
|
150
|
+
try:
|
|
151
|
+
with urllib.request.urlopen(req, timeout=175) as r:
|
|
152
|
+
return r.status, json.load(r)
|
|
153
|
+
except urllib.error.HTTPError as e:
|
|
154
|
+
try:
|
|
155
|
+
return e.code, json.load(e)
|
|
156
|
+
except Exception:
|
|
157
|
+
return e.code, {"detail": e.reason}
|
|
158
|
+
except Exception as e: # network / timeout
|
|
159
|
+
return 0, {"detail": f"could not reach Zetesis ({type(e).__name__})"}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def main():
|
|
163
|
+
mcp.run()
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
if __name__ == "__main__":
|
|
167
|
+
main()
|