solvapay 0.1.0.dev4__py3-none-any.whl
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.
- solvapay/__init__.py +26 -0
- solvapay/_currencies.py +170 -0
- solvapay/_generated/__init__.py +1 -0
- solvapay/_generated/models.py +3643 -0
- solvapay/_generated/operations.py +1894 -0
- solvapay/_http.py +243 -0
- solvapay/_js.py +114 -0
- solvapay/_tax_ids.py +442 -0
- solvapay/agents/__init__.py +67 -0
- solvapay/agents/calls.py +302 -0
- solvapay/agents/identity.py +293 -0
- solvapay/agents/mcp.py +614 -0
- solvapay/agents/oauth.py +105 -0
- solvapay/business_details.py +124 -0
- solvapay/client.py +107 -0
- solvapay/errors.py +33 -0
- solvapay/mcp/__init__.py +90 -0
- solvapay/mcp/app.py +243 -0
- solvapay/mcp/auth.py +604 -0
- solvapay/mcp/bootstrap.py +494 -0
- solvapay/mcp/intent.py +474 -0
- solvapay/mcp/mcp-app.html +119 -0
- solvapay/mcp/narrate.py +1019 -0
- solvapay/mcp/response.py +220 -0
- solvapay/mcp/server.py +343 -0
- solvapay/mcp/tools.py +273 -0
- solvapay/mcp/transport.py +550 -0
- solvapay/money.py +61 -0
- solvapay/paywall/__init__.py +25 -0
- solvapay/paywall/decide.py +450 -0
- solvapay/paywall/gate.py +176 -0
- solvapay/paywall/state.py +371 -0
- solvapay/pricing.py +281 -0
- solvapay/py.typed +0 -0
- solvapay/solvapay.py +173 -0
- solvapay/webhooks.py +93 -0
- solvapay-0.1.0.dev4.dist-info/METADATA +183 -0
- solvapay-0.1.0.dev4.dist-info/RECORD +39 -0
- solvapay-0.1.0.dev4.dist-info/WHEEL +4 -0
solvapay/agents/oauth.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""OAuth helpers for a server agents call: its resource URL and the RFC 9728 document.
|
|
2
|
+
|
|
3
|
+
Ported from ``@solvapay/agents``' ``oauth.ts``, which reads URLs as browsers do (the
|
|
4
|
+
WHATWG URL standard); the canonical form below follows it for the URLs a server
|
|
5
|
+
can have.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
from urllib.parse import quote, urljoin, urlsplit
|
|
12
|
+
|
|
13
|
+
AGENT_SCOPE = "usage"
|
|
14
|
+
METADATA_PREFIX = "/.well-known/oauth-protected-resource"
|
|
15
|
+
|
|
16
|
+
# What the URL standard leaves unescaped in a path and in a query (everything printable
|
|
17
|
+
# but space, ``"``, ``#``, ``<``, ``>``, and ``?``, `````, ``{``, ``}`` in a path or ``'`` in a
|
|
18
|
+
# query); ``%`` stays, so an escape is never escaped twice.
|
|
19
|
+
_PATH_SAFE = "!$%&'()*+,-./:;=@[\\]^_|~"
|
|
20
|
+
_QUERY_SAFE = "!$%&()*+,-./:;=?@[\\]^_`{|}~"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _remove_dot_segments(path: str) -> str:
|
|
24
|
+
"""RFC 3986 section 5.2.4: ``/a/../b/./c`` is ``/b/c``."""
|
|
25
|
+
out: list[str] = []
|
|
26
|
+
segments = path.split("/")
|
|
27
|
+
for i, segment in enumerate(segments):
|
|
28
|
+
last = i == len(segments) - 1
|
|
29
|
+
if segment == ".":
|
|
30
|
+
if last:
|
|
31
|
+
out.append("")
|
|
32
|
+
elif segment == "..":
|
|
33
|
+
if len(out) > 1:
|
|
34
|
+
out.pop()
|
|
35
|
+
if last:
|
|
36
|
+
out.append("")
|
|
37
|
+
else:
|
|
38
|
+
out.append(segment)
|
|
39
|
+
return "/".join(out)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def canonical_resource(resource: str) -> str:
|
|
43
|
+
"""The form SolvaPay compares resources in: scheme and host lowercased, default
|
|
44
|
+
port dropped, no trailing slash. A fragment or credentials make it unusable."""
|
|
45
|
+
parts = urlsplit(resource)
|
|
46
|
+
if parts.fragment or parts.username or parts.password:
|
|
47
|
+
raise ValueError(f"{resource} cannot be an OAuth resource: no fragment or credentials")
|
|
48
|
+
if not parts.scheme or not parts.hostname:
|
|
49
|
+
raise ValueError(f"{resource} is not an absolute URL")
|
|
50
|
+
scheme = parts.scheme.lower()
|
|
51
|
+
host = parts.hostname.lower()
|
|
52
|
+
if ":" in host:
|
|
53
|
+
host = f"[{host}]" # an IPv6 address keeps its brackets
|
|
54
|
+
default = {"http": 80, "https": 443}.get(scheme)
|
|
55
|
+
port = f":{parts.port}" if parts.port and parts.port != default else ""
|
|
56
|
+
path = quote(_remove_dot_segments(parts.path or "/"), safe=_PATH_SAFE).rstrip("/")
|
|
57
|
+
query = f"?{quote(parts.query, safe=_QUERY_SAFE)}" if parts.query else ""
|
|
58
|
+
return f"{scheme}://{host}{port}{path}{query}"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def protected_resource_metadata_path(resource: str, metadata_path: str | None = None) -> str:
|
|
62
|
+
"""Where the metadata is served: ``metadata_path``, else the RFC 9728 path-suffixed
|
|
63
|
+
form hosts try first, ``/.well-known/oauth-protected-resource/mcp``."""
|
|
64
|
+
if metadata_path:
|
|
65
|
+
return metadata_path
|
|
66
|
+
return f"{METADATA_PREFIX}{urlsplit(canonical_resource(resource)).path}"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def protected_resource_metadata_url(resource: str, metadata_path: str | None = None) -> str:
|
|
70
|
+
"""The absolute URL a challenge points hosts at, on the resource's origin."""
|
|
71
|
+
path = protected_resource_metadata_path(resource, metadata_path)
|
|
72
|
+
return urljoin(canonical_resource(resource), path)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def protected_resource_metadata(
|
|
76
|
+
*,
|
|
77
|
+
resource: str,
|
|
78
|
+
authorization_server: str,
|
|
79
|
+
server_name: str | None = None,
|
|
80
|
+
documentation: str | None = None,
|
|
81
|
+
) -> dict[str, Any]:
|
|
82
|
+
"""The RFC 9728 document naming SolvaPay's agent issuer as the authorization server."""
|
|
83
|
+
out: dict[str, Any] = {
|
|
84
|
+
"resource": canonical_resource(resource),
|
|
85
|
+
"authorization_servers": [authorization_server],
|
|
86
|
+
"scopes_supported": [AGENT_SCOPE],
|
|
87
|
+
"bearer_methods_supported": ["header"],
|
|
88
|
+
}
|
|
89
|
+
if server_name:
|
|
90
|
+
out["resource_name"] = server_name
|
|
91
|
+
if documentation:
|
|
92
|
+
out["resource_documentation"] = documentation
|
|
93
|
+
return out
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def challenge_header(*, resource: str, description: str, metadata_url: str | None = None) -> str:
|
|
97
|
+
"""The ``WWW-Authenticate`` value hosts look for, with ``error``, ``error_description``,
|
|
98
|
+
the metadata URL (default: the path-suffixed form on the resource's origin) and the
|
|
99
|
+
one scope."""
|
|
100
|
+
metadata = metadata_url or protected_resource_metadata_url(resource)
|
|
101
|
+
quoted = description.replace("\\", "\\\\").replace('"', '\\"')
|
|
102
|
+
return (
|
|
103
|
+
f'Bearer error="invalid_token", error_description="{quoted}", '
|
|
104
|
+
f'resource_metadata="{metadata}", scope="{AGENT_SCOPE}"'
|
|
105
|
+
)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""Business purchase details, checked and normalised as ``@solvapay/core``'s
|
|
2
|
+
``validateBusinessDetails`` does (``packages/core/src/business-details.ts``).
|
|
3
|
+
|
|
4
|
+
The tax-ID tables come from the TypeScript source (``_tax_ids.py``, generated
|
|
5
|
+
by ``scripts/tax_ids.mjs``), so both SDKs accept and reject the same input.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from ._tax_ids import (
|
|
13
|
+
BUYER_COUNTRIES,
|
|
14
|
+
COUNTRY_TO_TAX_ID_TYPE,
|
|
15
|
+
EU_MEMBER_COUNTRIES,
|
|
16
|
+
EU_VAT_FORMAT_BY_COUNTRY,
|
|
17
|
+
TAX_ID_FORMAT_BY_TYPE,
|
|
18
|
+
TAX_ID_TYPES,
|
|
19
|
+
)
|
|
20
|
+
from .errors import SolvaPayError
|
|
21
|
+
|
|
22
|
+
Json = dict[str, Any]
|
|
23
|
+
|
|
24
|
+
CUSTOMER_NAME_MAX = 100
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"TAX_ID_TYPES",
|
|
28
|
+
"is_buyer_country",
|
|
29
|
+
"is_valid_tax_id_for_country",
|
|
30
|
+
"normalize_tax_id",
|
|
31
|
+
"validate_business_details",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def is_buyer_country(value: str) -> bool:
|
|
36
|
+
"""A country Stripe Tax calculates tax for, as a buyer's country."""
|
|
37
|
+
return value.strip().upper() in BUYER_COUNTRIES
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def normalize_tax_id(tax_id: str) -> str:
|
|
41
|
+
return "".join(tax_id.strip().upper().split())
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def is_valid_tax_id_for_country(country: str, tax_id: str) -> bool:
|
|
45
|
+
tax_id_type = COUNTRY_TO_TAX_ID_TYPE.get(country)
|
|
46
|
+
if tax_id_type is None:
|
|
47
|
+
return False
|
|
48
|
+
normalized = normalize_tax_id(tax_id)
|
|
49
|
+
if tax_id_type == "eu_vat" and country in EU_MEMBER_COUNTRIES:
|
|
50
|
+
return EU_VAT_FORMAT_BY_COUNTRY[country].fullmatch(normalized) is not None
|
|
51
|
+
return TAX_ID_FORMAT_BY_TYPE[tax_id_type].fullmatch(normalized) is not None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _stripped(value: str | None) -> str:
|
|
55
|
+
return value.strip() if value else ""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _invalid(message: str) -> SolvaPayError:
|
|
59
|
+
return SolvaPayError(message, code="invalid_business_details", status=400)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def validate_business_details(
|
|
63
|
+
*,
|
|
64
|
+
is_business: bool,
|
|
65
|
+
business_name: str | None = None,
|
|
66
|
+
country: str | None = None,
|
|
67
|
+
customer_country: str | None = None,
|
|
68
|
+
customer_name: str | None = None,
|
|
69
|
+
customer_state: str | None = None,
|
|
70
|
+
customer_postal_code: str | None = None,
|
|
71
|
+
tax_id: str | None = None,
|
|
72
|
+
) -> Json:
|
|
73
|
+
"""The details to send the API, with camelCase keys.
|
|
74
|
+
|
|
75
|
+
Raises ``SolvaPayError`` (code ``invalid_business_details``, status 400)
|
|
76
|
+
with the first problem as its message, the one TypeScript reports.
|
|
77
|
+
"""
|
|
78
|
+
if customer_name is not None and len(customer_name) > CUSTOMER_NAME_MAX:
|
|
79
|
+
raise _invalid(f"Too big: expected string to have <={CUSTOMER_NAME_MAX} characters")
|
|
80
|
+
|
|
81
|
+
name = _stripped(customer_name)
|
|
82
|
+
address: Json = {}
|
|
83
|
+
if _stripped(customer_state):
|
|
84
|
+
address["customerState"] = _stripped(customer_state)
|
|
85
|
+
if _stripped(customer_postal_code):
|
|
86
|
+
address["customerPostalCode"] = _stripped(customer_postal_code)
|
|
87
|
+
|
|
88
|
+
if not is_business:
|
|
89
|
+
billing_country = _stripped(customer_country).upper()
|
|
90
|
+
if billing_country and not is_buyer_country(billing_country):
|
|
91
|
+
raise _invalid("Billing country is not supported for tax calculation")
|
|
92
|
+
if billing_country:
|
|
93
|
+
consumer: Json = {"isBusiness": False, "customerCountry": billing_country}
|
|
94
|
+
if name:
|
|
95
|
+
consumer["customerName"] = name
|
|
96
|
+
return {**consumer, **address}
|
|
97
|
+
return {"isBusiness": False, **({"customerName": name} if name else {})}
|
|
98
|
+
|
|
99
|
+
business_country = _stripped(country).upper()
|
|
100
|
+
if not business_country:
|
|
101
|
+
raise _invalid("Country is required")
|
|
102
|
+
if not is_buyer_country(business_country):
|
|
103
|
+
raise _invalid("Country is not supported for business purchases")
|
|
104
|
+
if _stripped(tax_id) and not is_valid_tax_id_for_country(business_country, tax_id or ""):
|
|
105
|
+
raise _invalid(f"Enter a valid tax ID for {business_country}")
|
|
106
|
+
|
|
107
|
+
billing_country = _stripped(customer_country).upper()
|
|
108
|
+
details: Json = {
|
|
109
|
+
"isBusiness": True,
|
|
110
|
+
"country": business_country,
|
|
111
|
+
"customerCountry": billing_country
|
|
112
|
+
if billing_country and is_buyer_country(billing_country)
|
|
113
|
+
else business_country,
|
|
114
|
+
}
|
|
115
|
+
if _stripped(business_name):
|
|
116
|
+
details["businessName"] = _stripped(business_name)
|
|
117
|
+
if _stripped(tax_id):
|
|
118
|
+
details["taxId"] = normalize_tax_id(tax_id or "")
|
|
119
|
+
tax_id_type = COUNTRY_TO_TAX_ID_TYPE.get(business_country)
|
|
120
|
+
if tax_id_type:
|
|
121
|
+
details["taxIdType"] = tax_id_type
|
|
122
|
+
if name:
|
|
123
|
+
details["customerName"] = name
|
|
124
|
+
return {**details, **address}
|
solvapay/client.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""The SolvaPay API client: every operation in the spec, async or blocking.
|
|
2
|
+
|
|
3
|
+
```python
|
|
4
|
+
from solvapay import AsyncApiClient
|
|
5
|
+
|
|
6
|
+
async with AsyncApiClient(api_key="sk_sandbox_...") as api:
|
|
7
|
+
customer = await api.get_customer(reference="cus_123")
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
The methods are generated from ``openapi/sdk-v1.json``; this module only
|
|
11
|
+
adds construction and lifecycle.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from types import TracebackType
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
import httpx
|
|
20
|
+
|
|
21
|
+
from ._generated.operations import AsyncSolvaPayApi, SolvaPayApi
|
|
22
|
+
from ._http import (
|
|
23
|
+
DEFAULT_API_BASE_URL,
|
|
24
|
+
DEFAULT_MAX_RETRIES,
|
|
25
|
+
DEFAULT_TIMEOUT_SECONDS,
|
|
26
|
+
AsyncTransport,
|
|
27
|
+
SyncTransport,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
__all__ = ["ApiClient", "AsyncApiClient"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class AsyncApiClient(AsyncSolvaPayApi):
|
|
34
|
+
"""Async client. Use it as an async context manager, or call ``aclose()``."""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
*,
|
|
39
|
+
api_key: str,
|
|
40
|
+
api_base_url: str = DEFAULT_API_BASE_URL,
|
|
41
|
+
timeout: float = DEFAULT_TIMEOUT_SECONDS,
|
|
42
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
43
|
+
http_client: httpx.AsyncClient | None = None,
|
|
44
|
+
) -> None:
|
|
45
|
+
super().__init__(
|
|
46
|
+
AsyncTransport(
|
|
47
|
+
api_key=api_key,
|
|
48
|
+
api_base_url=api_base_url,
|
|
49
|
+
timeout=timeout,
|
|
50
|
+
max_retries=max_retries,
|
|
51
|
+
client=http_client,
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def api_base_url(self) -> str:
|
|
57
|
+
return self._transport.api_base_url
|
|
58
|
+
|
|
59
|
+
async def aclose(self) -> None:
|
|
60
|
+
await self._transport.aclose()
|
|
61
|
+
|
|
62
|
+
async def __aenter__(self) -> AsyncApiClient:
|
|
63
|
+
return self
|
|
64
|
+
|
|
65
|
+
async def __aexit__(
|
|
66
|
+
self,
|
|
67
|
+
exc_type: type[BaseException] | None,
|
|
68
|
+
exc: BaseException | None,
|
|
69
|
+
tb: TracebackType | None,
|
|
70
|
+
) -> None:
|
|
71
|
+
await self.aclose()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class ApiClient(SolvaPayApi):
|
|
75
|
+
"""Blocking client. Use it as a context manager, or call ``close()``."""
|
|
76
|
+
|
|
77
|
+
def __init__(
|
|
78
|
+
self,
|
|
79
|
+
*,
|
|
80
|
+
api_key: str,
|
|
81
|
+
api_base_url: str = DEFAULT_API_BASE_URL,
|
|
82
|
+
timeout: float = DEFAULT_TIMEOUT_SECONDS,
|
|
83
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
84
|
+
http_client: httpx.Client | None = None,
|
|
85
|
+
) -> None:
|
|
86
|
+
super().__init__(
|
|
87
|
+
SyncTransport(
|
|
88
|
+
api_key=api_key,
|
|
89
|
+
api_base_url=api_base_url,
|
|
90
|
+
timeout=timeout,
|
|
91
|
+
max_retries=max_retries,
|
|
92
|
+
client=http_client,
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def api_base_url(self) -> str:
|
|
98
|
+
return self._transport.api_base_url
|
|
99
|
+
|
|
100
|
+
def close(self) -> None:
|
|
101
|
+
self._transport.close()
|
|
102
|
+
|
|
103
|
+
def __enter__(self) -> ApiClient:
|
|
104
|
+
return self
|
|
105
|
+
|
|
106
|
+
def __exit__(self, *_: Any) -> None:
|
|
107
|
+
self.close()
|
solvapay/errors.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Errors raised by the SolvaPay SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SolvaPayError(Exception):
|
|
9
|
+
"""Every error the SDK raises.
|
|
10
|
+
|
|
11
|
+
Branch on ``code`` (a stable identifier) and ``status`` (the HTTP status,
|
|
12
|
+
when the error came from the API), never on the message, which is for
|
|
13
|
+
people and may change.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
message: str,
|
|
19
|
+
*,
|
|
20
|
+
code: str,
|
|
21
|
+
status: int | None = None,
|
|
22
|
+
body: Any = None,
|
|
23
|
+
) -> None:
|
|
24
|
+
super().__init__(message)
|
|
25
|
+
self.message = message
|
|
26
|
+
self.code = code
|
|
27
|
+
self.status = status
|
|
28
|
+
self.body = body
|
|
29
|
+
|
|
30
|
+
def __repr__(self) -> str:
|
|
31
|
+
return (
|
|
32
|
+
f"SolvaPayError(code={self.code!r}, status={self.status!r}, message={self.message!r})"
|
|
33
|
+
)
|
solvapay/mcp/__init__.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Paid MCP tools for the official MCP Python SDK. Install with ``pip install 'solvapay[mcp]'``."""
|
|
2
|
+
|
|
3
|
+
from .app import (
|
|
4
|
+
BOOTSTRAP_URI,
|
|
5
|
+
DEFAULT_CSP,
|
|
6
|
+
RESOURCE_MIME_TYPE,
|
|
7
|
+
HideToolsByAudience,
|
|
8
|
+
is_chatgpt_request,
|
|
9
|
+
merge_csp,
|
|
10
|
+
widget_html,
|
|
11
|
+
)
|
|
12
|
+
from .auth import SolvaPayTokenVerifier, authorization_server_metadata, oauth_bridge_routes
|
|
13
|
+
from .bootstrap import (
|
|
14
|
+
build_bootstrap_payload,
|
|
15
|
+
derive_default_view,
|
|
16
|
+
enrich_purchase,
|
|
17
|
+
select_active_plan_purchase,
|
|
18
|
+
)
|
|
19
|
+
from .intent import (
|
|
20
|
+
OVERVIEW_MARKDOWN,
|
|
21
|
+
OVERVIEW_URI,
|
|
22
|
+
build_solvapay_prompts,
|
|
23
|
+
narrated_tool_result,
|
|
24
|
+
register_intent_tools,
|
|
25
|
+
tool_error_result,
|
|
26
|
+
)
|
|
27
|
+
from .narrate import (
|
|
28
|
+
NARRATORS,
|
|
29
|
+
NarratorOutput,
|
|
30
|
+
narrate_activate_plan,
|
|
31
|
+
narrate_auto_recharge,
|
|
32
|
+
narrate_manage_account,
|
|
33
|
+
narrate_topup,
|
|
34
|
+
narrate_upgrade,
|
|
35
|
+
ui_placeholder,
|
|
36
|
+
)
|
|
37
|
+
from .response import CustomerSnapshot, Nudge, ResponseContext, ResponseResult
|
|
38
|
+
from .server import SolvaPayMcpServer, create_solvapay_mcp_server
|
|
39
|
+
from .tools import (
|
|
40
|
+
PAID_TOOL_HINT,
|
|
41
|
+
default_customer_ref,
|
|
42
|
+
free_tool_description_suffix,
|
|
43
|
+
register_free_tool,
|
|
44
|
+
register_payable_tool,
|
|
45
|
+
)
|
|
46
|
+
from .transport import TRANSPORT_TOOL_NAMES, register_transport_tools
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
"BOOTSTRAP_URI",
|
|
50
|
+
"DEFAULT_CSP",
|
|
51
|
+
"NARRATORS",
|
|
52
|
+
"OVERVIEW_MARKDOWN",
|
|
53
|
+
"OVERVIEW_URI",
|
|
54
|
+
"PAID_TOOL_HINT",
|
|
55
|
+
"RESOURCE_MIME_TYPE",
|
|
56
|
+
"TRANSPORT_TOOL_NAMES",
|
|
57
|
+
"CustomerSnapshot",
|
|
58
|
+
"HideToolsByAudience",
|
|
59
|
+
"NarratorOutput",
|
|
60
|
+
"Nudge",
|
|
61
|
+
"ResponseContext",
|
|
62
|
+
"ResponseResult",
|
|
63
|
+
"SolvaPayMcpServer",
|
|
64
|
+
"SolvaPayTokenVerifier",
|
|
65
|
+
"authorization_server_metadata",
|
|
66
|
+
"build_bootstrap_payload",
|
|
67
|
+
"build_solvapay_prompts",
|
|
68
|
+
"create_solvapay_mcp_server",
|
|
69
|
+
"default_customer_ref",
|
|
70
|
+
"derive_default_view",
|
|
71
|
+
"enrich_purchase",
|
|
72
|
+
"free_tool_description_suffix",
|
|
73
|
+
"is_chatgpt_request",
|
|
74
|
+
"merge_csp",
|
|
75
|
+
"narrate_activate_plan",
|
|
76
|
+
"narrate_auto_recharge",
|
|
77
|
+
"narrate_manage_account",
|
|
78
|
+
"narrate_topup",
|
|
79
|
+
"narrate_upgrade",
|
|
80
|
+
"narrated_tool_result",
|
|
81
|
+
"oauth_bridge_routes",
|
|
82
|
+
"register_free_tool",
|
|
83
|
+
"register_intent_tools",
|
|
84
|
+
"register_payable_tool",
|
|
85
|
+
"register_transport_tools",
|
|
86
|
+
"select_active_plan_purchase",
|
|
87
|
+
"tool_error_result",
|
|
88
|
+
"ui_placeholder",
|
|
89
|
+
"widget_html",
|
|
90
|
+
]
|