-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathpyproject.toml
More file actions
281 lines (258 loc) · 10.7 KB
/
pyproject.toml
File metadata and controls
281 lines (258 loc) · 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "adcp"
version = "5.2.0"
description = "Official Python client for the Ad Context Protocol (AdCP)"
authors = [
{name = "AdCP Community", email = "maintainers@adcontextprotocol.org"}
]
readme = "README.md"
requires-python = ">=3.10"
license = {text = "Apache-2.0"}
keywords = ["adcp", "mcp", "a2a", "protocol", "advertising"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development :: Libraries :: Python Modules",
]
dependencies = [
"httpx>=0.24.0",
# httpcore is a transitive dep of httpx, but we import from it
# directly (``adcp.signing.ip_pinned_transport`` subclasses
# ``SyncBackend`` / ``AnyIOBackend`` from ``httpcore._backends.*``).
# Pin the major so a breaking upgrade fails at install rather than
# at runtime. The contract test in
# ``tests/conformance/signing/test_ip_pinned_transport_contract.py``
# guards the specific API shapes we rely on.
"httpcore>=1.0,<2.0",
# Upper bound is load-bearing for ``adcp.types.error_narrowing``,
# which depends on pydantic-2 ValidationError.errors() internals
# (``err[\"type\"]`` literals like ``\"literal_error\"`` /
# ``\"union_tag_not_found\"`` and CamelCase variant names interleaved
# in ``err[\"loc\"]``). Pydantic 3 has no API guarantee on these
# internals; bump only after porting the narrowing heuristics.
"pydantic>=2.0.0,<3",
"typing-extensions>=4.5.0",
# A2A protocol v1.0 (protobuf types, ProtoJSON on the wire). We run
# on the v1.0 Python SDK with ``enable_v0_3_compat=True`` on the
# server-side JSON-RPC route factory, which dual-serves the AgentCard
# and preserves 0.3 JSON shapes outbound for existing 0.3 clients.
# No coordinated buyer migration needed.
# Upper bound: 1.0.2 reads ``field.label`` on the upb FieldDescriptor,
# which protobuf 7.x no longer exposes — every A2A ``message/send``
# decorator (``@validate_proto_required_fields``) blows up with
# ``'_upb._message.FieldDescriptor' object has no attribute 'label'``.
# Lower bound: 1.0.1 reads ``field.is_repeated`` on the same descriptor,
# which protobuf 5.x doesn't expose — same decorator, mirror failure
# mode (``no attribute 'is_repeated'``). The protobuf floor below
# closes the 5.x cell so this pin is safe.
"a2a-sdk>=1.0.1,<1.0.2",
# Floor coupled to ``a2a-sdk`` above. a2a-sdk 1.0.1 reads
# ``field.is_repeated`` on the upb FieldDescriptor inside
# ``@validate_proto_required_fields`` — the attribute exists on the
# upb extension only from protobuf 6.x onward. With protobuf 5.x
# adopters land in a broken cell where every ``message/send`` raises
# ``AttributeError`` before any handler runs. Direct constraint here
# rather than relying on a2a-sdk's transitive pin (which is wider).
"protobuf>=6,<8",
"sse-starlette>=2.0", # required by a2a-sdk v0.3 compat adapter
# 1.27.0 added ``session_idle_timeout`` to ``StreamableHTTPSessionManager``
# which we pass when adopters opt into stateful streamable-http. Upper
# bound at <2.0 because ``adcp.server.serve.create_mcp_server`` reads
# FastMCP private attrs (``_mcp_server``, ``_event_store``,
# ``_retry_interval``) when pre-creating the session manager — that
# contract is not preserved across majors, and upstream signaled v2
# development on ``main`` (v1.x maintenance branch only ports critical
# fixes). Bump deliberately when v2 lands.
"mcp>=1.27.0,<2.0",
"email-validator>=2.0.0",
"cryptography>=41.0.0",
# RFC 8785 JSON Canonicalization Scheme — used by the server-side
# idempotency middleware to compute the spec-mandated payload hash for
# replay detection. Tiny pure-Python dep, no transitive weight.
"rfc8785>=0.1.4",
# JSON Schema validation — backs adcp.validation.schema_validator for
# pre-send request / post-receive response drift detection against the
# bundled AdCP schemas. Pin to the Draft 7 generation the schemas
# declare via ``$schema``.
"jsonschema>=4.0.0",
]
[project.scripts]
adcp = "adcp.__main__:main"
adcp-keygen = "adcp.signing.keygen:main"
[project.optional-dependencies]
dev = [
"pytest>=7.0.0",
"pytest-asyncio>=0.21.0",
"pytest-cov>=4.0.0",
"mypy>=1.20.0,<1.21",
"black>=23.0.0",
"ruff>=0.1.0",
# Pin to exact version: codegen's variant numbering (e.g. CreateMediaBuyResponse1 vs
# CreateMediaBuyResponse) shifts between versions, producing diff churn and breaking
# generated-code imports that reference specific suffixes.
"datamodel-code-generator[http]==0.56.1",
# Runs Starlette app lifespan under httpx.ASGITransport in tests —
# the canonical library for what httpx doesn't do natively. Used by
# tests/test_mcp_middleware_composition.py and future integration
# tests that exercise the streamable-HTTP ASGI app in-process.
"asgi-lifespan>=2.1.0",
# mypy stubs for the protobuf runtime we use via a2a-sdk 1.0
# (``google.protobuf.json_format``, ``struct_pb2``, ``timestamp_pb2``).
# Without these mypy flags every import as ``import-untyped``.
"types-protobuf>=7.34.1.20260408",
# Mocks httpx in the v3 reference seller's translator-pattern tests
# so we don't need to boot the JS mock-server in the Python pytest
# CI run.
"respx>=0.20.0",
]
docs = [
"pdoc3>=0.10.0",
]
pg = [
# PostgreSQL-backed adcp.signing.PgReplayStore,
# adcp.decisioning.PostgresTaskRegistry (durable HITL task state), and
# future PgIdempotencyBackend. psycopg3 ships both sync + async pool
# interfaces so the single dep serves all three use cases.
"psycopg[binary]>=3.1.0",
"psycopg-pool>=3.2.0",
]
[project.urls]
Homepage = "https://github.com/adcontextprotocol/adcp-client-python"
Documentation = "https://docs.adcontextprotocol.org"
Repository = "https://github.com/adcontextprotocol/adcp-client-python"
Issues = "https://github.com/adcontextprotocol/adcp-client-python/issues"
[tool.setuptools.packages.find]
where = ["src"]
[tool.setuptools.package-data]
adcp = [
"py.typed",
"ADCP_VERSION",
"signing/pg/*.sql",
"decisioning/pg/*.sql",
# AdCP JSON schemas, mirrored from ``schemas/cache/`` by
# ``scripts/bundle_schemas.py`` so the wheel ships them for
# ``adcp.validation.schema_loader``.
"_schemas/**/*.json",
]
# Named schemas bundled directly with the SDK (committed, not generated).
# These live in src/adcp/schemas/ and are loaded via adcp.schemas.load_schema.
"adcp.schemas" = ["*.json"]
[tool.black]
line-length = 100
target-version = ["py310", "py311", "py312"]
# Skip auto-generated files. Running black on generated_poc/ during
# pre-commit after `make regenerate-schemas` causes a stash conflict
# (hook reformats, tries to re-apply over seller's already-modified
# files, rolls back). The regen script already runs black on the
# consolidated _generated.py; individual generated_poc/ files don't
# need formatting at commit time. Mirrors the ruff extend-exclude.
extend-exclude = "/(tasks|_generated|registry)\\.py$|/types/generated_poc/"
[tool.ruff]
line-length = 100
target-version = "py310"
extend-exclude = [
"src/adcp/types/_generated.py",
"src/adcp/types/registry.py",
"src/adcp/types/tasks.py",
"src/adcp/types/generated_poc/",
"examples/",
"scripts/",
]
[tool.ruff.lint]
select = ["E", "F", "I", "N", "W", "UP"]
ignore = [
"E402", # Allow imports after module docstrings
"UP038", # isinstance() doesn't support X | Y syntax, only type hints do
]
[tool.mypy]
python_version = "3.10"
strict = true
warn_return_any = true
warn_unused_configs = true
[[tool.mypy.overrides]]
module = "tests.*"
disable_error_code = ["import-not-found", "no-untyped-def", "var-annotated", "operator"]
[[tool.mypy.overrides]]
module = "adcp.types.generated_poc.*"
disable_error_code = ["valid-type"]
[[tool.mypy.overrides]]
module = "tests.type_checks.*"
# Re-enable codes silenced by the broader tests.* override above.
# These files are the adopter-pattern contract: every file must pass
# mypy --strict with zero # type: ignore lines.
enable_error_code = ["no-untyped-def", "var-annotated", "operator", "import-not-found"]
[[tool.mypy.overrides]]
module = "tests.integration.*"
ignore_errors = true
# psycopg is an optional dep behind the [pg] extra; type stubs aren't
# guaranteed to be present when the base SDK is installed.
[[tool.mypy.overrides]]
module = ["psycopg", "psycopg.*", "psycopg_pool", "psycopg_pool.*"]
ignore_missing_imports = true
# jsonschema doesn't ship PEP 561 stubs in the default package; the
# ``types-jsonschema`` stub package is optional and we use
# :class:`Any`-typed returns across the validator boundary anyway.
[[tool.mypy.overrides]]
module = ["jsonschema", "jsonschema.*"]
ignore_missing_imports = true
[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"
markers = [
"unit: Unit tests that don't require external services",
"integration: Integration tests that hit real endpoints (may be slow/flaky)",
"slow: Tests that take significant time to run",
]
# By default, skip integration tests for fast local development
addopts = "-m 'not integration'"
[tool.coverage.run]
source = ["src/adcp"]
omit = [
"*/tests/*",
"*/test_*.py",
]
branch = true
[tool.coverage.report]
precision = 2
show_missing = true
skip_covered = false
exclude_lines = [
"pragma: no cover",
"def __repr__",
"raise AssertionError",
"raise NotImplementedError",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]
# Maintain current 86% coverage, fail if it drops below 80%
fail_under = 80
[tool.coverage.html]
directory = "htmlcov"
[tool.bandit]
exclude_dirs = ["tests", "scripts"]
skips = ["B101"] # Allow assert in code (we're not using -O optimization)
[dependency-groups]
dev = [
"datamodel-code-generator==0.56.1",
"pre-commit>=4.4.0",
"types-protobuf>=7.34.1.20260408",
# Pinned in the dev group so ``uv run mypy`` (used by the pre-commit
# hook) installs mypy into the project venv instead of falling
# through to whatever ``mypy`` happens to be on $PATH (e.g. a
# homebrew install). Without this, pre-commit's mypy version drifts
# from CI's, producing a different error count than CI on the same
# source — and a dead-weight hook that everyone bypasses with
# ``SKIP=mypy``.
"mypy>=1.20.0",
]