testmcpy-oauth-probe 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.
Files changed (24) hide show
  1. testmcpy_oauth_probe-0.1.0/PKG-INFO +66 -0
  2. testmcpy_oauth_probe-0.1.0/README.md +44 -0
  3. testmcpy_oauth_probe-0.1.0/pyproject.toml +48 -0
  4. testmcpy_oauth_probe-0.1.0/setup.cfg +4 -0
  5. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe/__init__.py +44 -0
  6. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe/__main__.py +3 -0
  7. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe/cli.py +152 -0
  8. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe/config.py +426 -0
  9. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe/discovery.py +733 -0
  10. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe/mcp.py +332 -0
  11. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe/models.py +231 -0
  12. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe/oauth.py +583 -0
  13. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe/reporters.py +117 -0
  14. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe/runner.py +243 -0
  15. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe/schemas/oauth-smoke-report-v1.schema.json +78 -0
  16. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe/schemas/oauth-smoke-v1.schema.json +468 -0
  17. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe/secrets.py +118 -0
  18. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe/transport.py +225 -0
  19. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe.egg-info/PKG-INFO +66 -0
  20. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe.egg-info/SOURCES.txt +22 -0
  21. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe.egg-info/dependency_links.txt +1 -0
  22. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe.egg-info/entry_points.txt +2 -0
  23. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe.egg-info/requires.txt +2 -0
  24. testmcpy_oauth_probe-0.1.0/testmcpy_oauth_probe.egg-info/top_level.txt +1 -0
@@ -0,0 +1,66 @@
1
+ Metadata-Version: 2.4
2
+ Name: testmcpy-oauth-probe
3
+ Version: 0.1.0
4
+ Summary: Headless, vendor-neutral OAuth and MCP interoperability probe
5
+ Author: Amin Ghadersohi
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/preset-io/testmcpy
8
+ Project-URL: Repository, https://github.com/preset-io/testmcpy
9
+ Project-URL: Issues, https://github.com/preset-io/testmcpy/issues
10
+ Keywords: oauth,mcp,ci,smoke-test,interoperability
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Software Development :: Testing
18
+ Requires-Python: <3.13,>=3.10
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: httpx<1.0.0,>=0.27.0
21
+ Requires-Dist: pyyaml<7.0,>=6.0
22
+
23
+ # testmcpy-oauth-probe
24
+
25
+ The independently installable, headless distribution of testmcpy's OAuth/MCP
26
+ interoperability probe. It depends on **HTTPX and PyYAML only** — installing it
27
+ into a release pipeline will not pull in an agent runtime, a web server or an
28
+ ORM, and so cannot conflict with whatever that pipeline already pins.
29
+
30
+ ```bash
31
+ pip install "testmcpy-oauth-probe==0.1.0"
32
+ ```
33
+
34
+ This distribution owns the `testmcpy-oauth` console script. The main `testmcpy`
35
+ wheel depends on this one and exposes the same core as `testmcpy auth`; it does
36
+ not ship a second copy of the package, so installing both is safe.
37
+
38
+ It reports observable interoperability and configured policy outcomes. It is
39
+ not a formal OAuth or MCP compliance certification.
40
+
41
+ ```bash
42
+ testmcpy-oauth validate --config auth-smoke.yaml
43
+ testmcpy-oauth schema --kind report > oauth-smoke-report-v1.schema.json
44
+ testmcpy-oauth check --config auth-smoke.yaml --format json --output report.json
45
+ ```
46
+
47
+ `--config` and `--manifest` are accepted interchangeably.
48
+
49
+ `--service`, `--region`, `--revision` and `--deployment-id` are report labels
50
+ recorded in the `correlation` block. They are never compared against the
51
+ deployed target; the probe does not assert the running revision.
52
+
53
+ ## Versioning
54
+
55
+ Versioned independently of `testmcpy`, because consumers pin it into release
56
+ pipelines and rely on its dependency closure staying at two libraries. The
57
+ manifest and report schemas carry their own version strings
58
+ (`testmcpy.io/oauth-smoke/v1`, `testmcpy.io/oauth-smoke-report/v1`) and do not
59
+ move with this package's version.
60
+
61
+ ## Provenance
62
+
63
+ CI builds publish a `testmcpy-oauth-probe-<full Git SHA>` artifact containing
64
+ the wheel, source archive, `SOURCE_COMMIT`, and `SHA256SUMS`. Pin the full
65
+ commit/artifact name, confirm `SOURCE_COMMIT`, and verify the checksums before
66
+ installation.
@@ -0,0 +1,44 @@
1
+ # testmcpy-oauth-probe
2
+
3
+ The independently installable, headless distribution of testmcpy's OAuth/MCP
4
+ interoperability probe. It depends on **HTTPX and PyYAML only** — installing it
5
+ into a release pipeline will not pull in an agent runtime, a web server or an
6
+ ORM, and so cannot conflict with whatever that pipeline already pins.
7
+
8
+ ```bash
9
+ pip install "testmcpy-oauth-probe==0.1.0"
10
+ ```
11
+
12
+ This distribution owns the `testmcpy-oauth` console script. The main `testmcpy`
13
+ wheel depends on this one and exposes the same core as `testmcpy auth`; it does
14
+ not ship a second copy of the package, so installing both is safe.
15
+
16
+ It reports observable interoperability and configured policy outcomes. It is
17
+ not a formal OAuth or MCP compliance certification.
18
+
19
+ ```bash
20
+ testmcpy-oauth validate --config auth-smoke.yaml
21
+ testmcpy-oauth schema --kind report > oauth-smoke-report-v1.schema.json
22
+ testmcpy-oauth check --config auth-smoke.yaml --format json --output report.json
23
+ ```
24
+
25
+ `--config` and `--manifest` are accepted interchangeably.
26
+
27
+ `--service`, `--region`, `--revision` and `--deployment-id` are report labels
28
+ recorded in the `correlation` block. They are never compared against the
29
+ deployed target; the probe does not assert the running revision.
30
+
31
+ ## Versioning
32
+
33
+ Versioned independently of `testmcpy`, because consumers pin it into release
34
+ pipelines and rely on its dependency closure staying at two libraries. The
35
+ manifest and report schemas carry their own version strings
36
+ (`testmcpy.io/oauth-smoke/v1`, `testmcpy.io/oauth-smoke-report/v1`) and do not
37
+ move with this package's version.
38
+
39
+ ## Provenance
40
+
41
+ CI builds publish a `testmcpy-oauth-probe-<full Git SHA>` artifact containing
42
+ the wheel, source archive, `SOURCE_COMMIT`, and `SHA256SUMS`. Pin the full
43
+ commit/artifact name, confirm `SOURCE_COMMIT`, and verify the checksums before
44
+ installation.
@@ -0,0 +1,48 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "testmcpy-oauth-probe"
7
+ # Versioned independently of `testmcpy`. This distribution is release-pipeline
8
+ # infrastructure: consumers pin it exactly and expect its dependency closure to
9
+ # stay at two libraries, so it must not inherit testmcpy's release cadence.
10
+ version = "0.1.0"
11
+ description = "Headless, vendor-neutral OAuth and MCP interoperability probe"
12
+ readme = "README.md"
13
+ authors = [{name = "Amin Ghadersohi"}]
14
+ requires-python = ">=3.10,<3.13"
15
+ license = "Apache-2.0"
16
+ keywords = ["oauth", "mcp", "ci", "smoke-test", "interoperability"]
17
+ classifiers = [
18
+ "Development Status :: 4 - Beta",
19
+ "Intended Audience :: Developers",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Topic :: Software Development :: Testing",
25
+ ]
26
+ # Two libraries, deliberately. Consumers install this into release pipelines
27
+ # that pin conflicting versions of the agent/server stack (sqlalchemy, fastmcp),
28
+ # so anything added here has to survive being resolved against someone else's
29
+ # lockfile. `unit_tests/test_oauth_probe_packaging.py` enforces the closure.
30
+ dependencies = [
31
+ "httpx>=0.27.0,<1.0.0",
32
+ "pyyaml>=6.0,<7.0",
33
+ ]
34
+
35
+ [project.scripts]
36
+ testmcpy-oauth = "testmcpy_oauth_probe.cli:main"
37
+
38
+ [project.urls]
39
+ Homepage = "https://github.com/preset-io/testmcpy"
40
+ Repository = "https://github.com/preset-io/testmcpy"
41
+ Issues = "https://github.com/preset-io/testmcpy/issues"
42
+
43
+ [tool.setuptools.packages.find]
44
+ where = ["."]
45
+ include = ["testmcpy_oauth_probe*"]
46
+
47
+ [tool.setuptools.package-data]
48
+ testmcpy_oauth_probe = ["schemas/*.json"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,44 @@
1
+ """Typed public API for the headless OAuth/MCP probe."""
2
+
3
+ from testmcpy_oauth_probe.config import (
4
+ ConfigError,
5
+ load_manifest,
6
+ loads_manifest,
7
+ manifest_json_schema,
8
+ report_json_schema,
9
+ )
10
+ from testmcpy_oauth_probe.models import (
11
+ CONFIG_SCHEMA,
12
+ REPORT_SCHEMA,
13
+ AuthFlow,
14
+ CapabilityPolicy,
15
+ CheckResult,
16
+ CheckStatus,
17
+ Manifest,
18
+ ProbeReport,
19
+ RunReport,
20
+ TargetConfig,
21
+ )
22
+ from testmcpy_oauth_probe.runner import ProbeRunner, run_manifest
23
+ from testmcpy_oauth_probe.transport import HttpTransport
24
+
25
+ __all__ = [
26
+ "CONFIG_SCHEMA",
27
+ "REPORT_SCHEMA",
28
+ "AuthFlow",
29
+ "CapabilityPolicy",
30
+ "CheckResult",
31
+ "CheckStatus",
32
+ "ConfigError",
33
+ "Manifest",
34
+ "HttpTransport",
35
+ "ProbeReport",
36
+ "ProbeRunner",
37
+ "RunReport",
38
+ "TargetConfig",
39
+ "load_manifest",
40
+ "loads_manifest",
41
+ "manifest_json_schema",
42
+ "report_json_schema",
43
+ "run_manifest",
44
+ ]
@@ -0,0 +1,3 @@
1
+ from testmcpy_oauth_probe.cli import main
2
+
3
+ raise SystemExit(main())
@@ -0,0 +1,152 @@
1
+ """Minimal argparse CLI, independent of testmcpy's UI/LLM dependencies."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import asyncio
7
+ import sys
8
+ from collections.abc import Sequence
9
+ from pathlib import Path
10
+
11
+ from testmcpy_oauth_probe.config import (
12
+ ConfigError,
13
+ dump_manifest_schema,
14
+ dump_report_schema,
15
+ load_manifest,
16
+ loads_manifest,
17
+ )
18
+ from testmcpy_oauth_probe.models import CONFIG_SCHEMA, Correlation
19
+ from testmcpy_oauth_probe.reporters import to_human, to_json, to_jsonl, to_junit
20
+ from testmcpy_oauth_probe.runner import ProbeRunner
21
+
22
+ # `--config` stays first so it remains the name in `--help` output and in every
23
+ # existing pipeline; `--manifest` is an accepted alias. argparse stores both
24
+ # under `dest="config"`.
25
+ _MANIFEST_FLAGS = ("--config", "--manifest")
26
+ _MANIFEST_KWARGS = {
27
+ "dest": "config",
28
+ "metavar": "PATH",
29
+ "help": "Path to the versioned probe manifest (YAML or JSON)",
30
+ }
31
+
32
+
33
+ def build_parser() -> argparse.ArgumentParser:
34
+ parser = argparse.ArgumentParser(
35
+ prog="testmcpy-oauth",
36
+ description="Vendor-neutral OAuth/MCP interoperability probe (not formal certification)",
37
+ )
38
+ subparsers = parser.add_subparsers(dest="command", required=True)
39
+ validate = subparsers.add_parser("validate", help="Validate a manifest without network access")
40
+ # Everything else — the docs, the schema name, the Auth Smoke page, this
41
+ # tool's own help text — calls the file a manifest, so `--manifest` is the
42
+ # flag people reach for first. Accept both rather than make them guess.
43
+ validate.add_argument(*_MANIFEST_FLAGS, required=True, **_MANIFEST_KWARGS)
44
+ schema = subparsers.add_parser("schema", help="Print a versioned JSON Schema")
45
+ schema.add_argument("--kind", choices=("manifest", "report"), default="manifest")
46
+ schema.set_defaults(command="schema")
47
+ check = subparsers.add_parser("check", help="Run configured targets headlessly")
48
+ check.add_argument(*_MANIFEST_FLAGS, required=True, **_MANIFEST_KWARGS)
49
+ check.add_argument("--target", action="append", dest="targets")
50
+ check.add_argument("--profile")
51
+ check.add_argument("--format", choices=("human", "json", "jsonl"), default="human")
52
+ check.add_argument("--output", default="-")
53
+ check.add_argument("--junit")
54
+ check.add_argument("--run-id")
55
+ check.add_argument("--service", help="Report label only: recorded in correlation.service.")
56
+ check.add_argument("--region", help="Report label only: recorded in correlation.region.")
57
+ check.add_argument(
58
+ "--revision",
59
+ help=(
60
+ "Report label only: recorded in correlation.revision and never "
61
+ "compared against the deployed revision. This is not a revision assertion."
62
+ ),
63
+ )
64
+ check.add_argument(
65
+ "--deployment-id", help="Report label only: recorded in correlation.deployment_id."
66
+ )
67
+ discover = subparsers.add_parser("discover", help="Run discovery without credentials")
68
+ discover.add_argument("--url", required=True)
69
+ discover.add_argument("--format", choices=("human", "json", "jsonl"), default="human")
70
+ return parser
71
+
72
+
73
+ def _write(path: str, content: str) -> None:
74
+ if path == "-":
75
+ sys.stdout.write(content)
76
+ else:
77
+ Path(path).write_text(content, encoding="utf-8")
78
+
79
+
80
+ def _render(report_format: str, report: object) -> str:
81
+ from testmcpy_oauth_probe.models import RunReport
82
+
83
+ assert isinstance(report, RunReport)
84
+ if report_format == "json":
85
+ return to_json(report)
86
+ if report_format == "jsonl":
87
+ return to_jsonl(report)
88
+ return to_human(report)
89
+
90
+
91
+ async def _run(args: argparse.Namespace) -> int:
92
+ if args.command == "discover":
93
+ manifest = loads_manifest(
94
+ f"""schema: {CONFIG_SCHEMA}
95
+ targets:
96
+ discovery:
97
+ mcp_url: {args.url!r}
98
+ oauth:
99
+ flow: none
100
+ # `discover` is advertised as read-only reconnaissance. error_probe
101
+ # defaults to true and now applies to every flow, so opt out explicitly
102
+ # rather than POST an unsupported grant to someone's token endpoint.
103
+ error_probe: false
104
+ """
105
+ )
106
+ report = await ProbeRunner().run_manifest(manifest)
107
+ _write("-", _render(args.format, report))
108
+ return report.exit_code
109
+ manifest = load_manifest(args.config)
110
+ report = await ProbeRunner().run_manifest(
111
+ manifest,
112
+ target_ids=args.targets,
113
+ profile=args.profile,
114
+ run_id=args.run_id,
115
+ correlation_override=Correlation(
116
+ service=args.service,
117
+ region=args.region,
118
+ revision=args.revision,
119
+ deployment_id=args.deployment_id,
120
+ ),
121
+ )
122
+ _write(args.output, _render(args.format, report))
123
+ if args.junit:
124
+ _write(args.junit, to_junit(report))
125
+ return report.exit_code
126
+
127
+
128
+ def main(argv: Sequence[str] | None = None) -> int:
129
+ parser = build_parser()
130
+ args = parser.parse_args(argv)
131
+ try:
132
+ if args.command == "schema":
133
+ print(dump_report_schema() if args.kind == "report" else dump_manifest_schema())
134
+ return 0
135
+ if args.command == "validate":
136
+ manifest = load_manifest(args.config)
137
+ print(
138
+ f"valid {manifest.schema} manifest: {len(manifest.targets)} target(s), "
139
+ f"{len(manifest.profiles)} profile(s)"
140
+ )
141
+ return 0
142
+ return asyncio.run(_run(args))
143
+ except (ConfigError, ValueError) as exc:
144
+ print(f"configuration error: {exc}", file=sys.stderr)
145
+ return 2
146
+ except KeyboardInterrupt:
147
+ print("interrupted", file=sys.stderr)
148
+ return 2
149
+
150
+
151
+ if __name__ == "__main__":
152
+ raise SystemExit(main())