adcp 0.1.2__py3-none-any.whl → 1.0.2__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.
adcp/__init__.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  """
2
4
  AdCP Python Client Library
3
5
 
@@ -6,13 +8,89 @@ Supports both A2A and MCP protocols with full type safety.
6
8
  """
7
9
 
8
10
  from adcp.client import ADCPClient, ADCPMultiAgentClient
9
- from adcp.types.core import AgentConfig, TaskResult, WebhookMetadata
11
+ from adcp.exceptions import (
12
+ ADCPAuthenticationError,
13
+ ADCPConnectionError,
14
+ ADCPError,
15
+ ADCPProtocolError,
16
+ ADCPTimeoutError,
17
+ ADCPToolNotFoundError,
18
+ ADCPWebhookError,
19
+ ADCPWebhookSignatureError,
20
+ )
21
+ from adcp.types.core import AgentConfig, Protocol, TaskResult, TaskStatus, WebhookMetadata
22
+ from adcp.types.generated import (
23
+ ActivateSignalRequest,
24
+ ActivateSignalResponse,
25
+ CreateMediaBuyRequest,
26
+ CreateMediaBuyResponse,
27
+ GetMediaBuyDeliveryRequest,
28
+ GetMediaBuyDeliveryResponse,
29
+ GetProductsRequest,
30
+ GetProductsResponse,
31
+ GetSignalsRequest,
32
+ GetSignalsResponse,
33
+ ListAuthorizedPropertiesRequest,
34
+ ListAuthorizedPropertiesResponse,
35
+ ListCreativeFormatsRequest,
36
+ ListCreativeFormatsResponse,
37
+ ListCreativesRequest,
38
+ ListCreativesResponse,
39
+ MediaBuy,
40
+ Product,
41
+ ProvidePerformanceFeedbackRequest,
42
+ ProvidePerformanceFeedbackResponse,
43
+ SyncCreativesRequest,
44
+ SyncCreativesResponse,
45
+ UpdateMediaBuyRequest,
46
+ UpdateMediaBuyResponse,
47
+ )
48
+
49
+ __version__ = "1.0.2"
10
50
 
11
- __version__ = "0.1.2"
12
51
  __all__ = [
52
+ # Client classes
13
53
  "ADCPClient",
14
54
  "ADCPMultiAgentClient",
55
+ # Core types
15
56
  "AgentConfig",
57
+ "Protocol",
16
58
  "TaskResult",
59
+ "TaskStatus",
17
60
  "WebhookMetadata",
61
+ # Exceptions
62
+ "ADCPError",
63
+ "ADCPConnectionError",
64
+ "ADCPAuthenticationError",
65
+ "ADCPTimeoutError",
66
+ "ADCPProtocolError",
67
+ "ADCPToolNotFoundError",
68
+ "ADCPWebhookError",
69
+ "ADCPWebhookSignatureError",
70
+ # Generated request/response types
71
+ "GetProductsRequest",
72
+ "GetProductsResponse",
73
+ "CreateMediaBuyRequest",
74
+ "CreateMediaBuyResponse",
75
+ "UpdateMediaBuyRequest",
76
+ "UpdateMediaBuyResponse",
77
+ "SyncCreativesRequest",
78
+ "SyncCreativesResponse",
79
+ "ListCreativesRequest",
80
+ "ListCreativesResponse",
81
+ "ListCreativeFormatsRequest",
82
+ "ListCreativeFormatsResponse",
83
+ "GetMediaBuyDeliveryRequest",
84
+ "GetMediaBuyDeliveryResponse",
85
+ "ListAuthorizedPropertiesRequest",
86
+ "ListAuthorizedPropertiesResponse",
87
+ "GetSignalsRequest",
88
+ "GetSignalsResponse",
89
+ "ActivateSignalRequest",
90
+ "ActivateSignalResponse",
91
+ "ProvidePerformanceFeedbackRequest",
92
+ "ProvidePerformanceFeedbackResponse",
93
+ # Core domain types
94
+ "Product",
95
+ "MediaBuy",
18
96
  ]
adcp/__main__.py ADDED
@@ -0,0 +1,284 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ """Command-line interface for AdCP client - compatible with npx @adcp/client."""
5
+
6
+ import argparse
7
+ import asyncio
8
+ import json
9
+ import sys
10
+ from pathlib import Path
11
+ from typing import Any, cast
12
+
13
+ from adcp.client import ADCPClient
14
+ from adcp.config import (
15
+ CONFIG_FILE,
16
+ get_agent,
17
+ list_agents,
18
+ remove_agent,
19
+ save_agent,
20
+ )
21
+ from adcp.types.core import AgentConfig, Protocol
22
+
23
+
24
+ def print_json(data: Any) -> None:
25
+ """Print data as JSON."""
26
+ print(json.dumps(data, indent=2, default=str))
27
+
28
+
29
+ def print_result(result: Any, json_output: bool = False) -> None:
30
+ """Print result in formatted or JSON mode."""
31
+ if json_output:
32
+ print_json(
33
+ {
34
+ "status": result.status.value,
35
+ "success": result.success,
36
+ "data": result.data,
37
+ "error": result.error,
38
+ "metadata": result.metadata,
39
+ "debug_info": {
40
+ "request": result.debug_info.request,
41
+ "response": result.debug_info.response,
42
+ "duration_ms": result.debug_info.duration_ms,
43
+ }
44
+ if result.debug_info
45
+ else None,
46
+ }
47
+ )
48
+ else:
49
+ print(f"\nStatus: {result.status.value}")
50
+ if result.success:
51
+ if result.data:
52
+ print("\nResult:")
53
+ print_json(result.data)
54
+ else:
55
+ print(f"Error: {result.error}")
56
+
57
+
58
+ async def execute_tool(
59
+ agent_config: dict[str, Any], tool_name: str, payload: dict[str, Any], json_output: bool = False
60
+ ) -> None:
61
+ """Execute a tool on an agent."""
62
+ # Ensure required fields
63
+ if "id" not in agent_config:
64
+ agent_config["id"] = agent_config.get("agent_uri", "unknown")
65
+
66
+ if "protocol" not in agent_config:
67
+ agent_config["protocol"] = "mcp"
68
+
69
+ # Convert string protocol to enum
70
+ if isinstance(agent_config["protocol"], str):
71
+ agent_config["protocol"] = Protocol(agent_config["protocol"].lower())
72
+
73
+ config = AgentConfig(**agent_config)
74
+
75
+ async with ADCPClient(config) as client:
76
+ result = await client.call_tool(tool_name, payload)
77
+ print_result(result, json_output)
78
+
79
+
80
+ def load_payload(payload_arg: str | None) -> dict[str, Any]:
81
+ """Load payload from argument (JSON, @file, or stdin)."""
82
+ if not payload_arg:
83
+ # Try to read from stdin if available and has data
84
+ if not sys.stdin.isatty():
85
+ try:
86
+ return cast(dict[str, Any], json.load(sys.stdin))
87
+ except (json.JSONDecodeError, ValueError):
88
+ pass
89
+ return {}
90
+
91
+ if payload_arg.startswith("@"):
92
+ # Load from file
93
+ file_path = Path(payload_arg[1:])
94
+ if not file_path.exists():
95
+ print(f"Error: File not found: {file_path}", file=sys.stderr)
96
+ sys.exit(1)
97
+ return cast(dict[str, Any], json.loads(file_path.read_text()))
98
+
99
+ # Parse as JSON
100
+ try:
101
+ return cast(dict[str, Any], json.loads(payload_arg))
102
+ except json.JSONDecodeError as e:
103
+ print(f"Error: Invalid JSON payload: {e}", file=sys.stderr)
104
+ sys.exit(1)
105
+
106
+
107
+ def handle_save_auth(alias: str, url: str | None, protocol: str | None) -> None:
108
+ """Handle --save-auth command."""
109
+ if not url:
110
+ # Interactive mode
111
+ url = input(f"Agent URL for '{alias}': ").strip()
112
+ if not url:
113
+ print("Error: URL is required", file=sys.stderr)
114
+ sys.exit(1)
115
+
116
+ if not protocol:
117
+ protocol = input("Protocol (mcp/a2a) [mcp]: ").strip() or "mcp"
118
+
119
+ auth_token = input("Auth token (optional): ").strip() or None
120
+
121
+ save_agent(alias, url, protocol, auth_token)
122
+ print(f"✓ Saved agent '{alias}'")
123
+
124
+
125
+ def handle_list_agents() -> None:
126
+ """Handle --list-agents command."""
127
+ agents = list_agents()
128
+
129
+ if not agents:
130
+ print("No saved agents")
131
+ return
132
+
133
+ print("\nSaved agents:")
134
+ for alias, config in agents.items():
135
+ auth = "yes" if config.get("auth_token") else "no"
136
+ print(f" {alias}")
137
+ print(f" URL: {config.get('agent_uri')}")
138
+ print(f" Protocol: {config.get('protocol', 'mcp').upper()}")
139
+ print(f" Auth: {auth}")
140
+
141
+
142
+ def handle_remove_agent(alias: str) -> None:
143
+ """Handle --remove-agent command."""
144
+ if remove_agent(alias):
145
+ print(f"✓ Removed agent '{alias}'")
146
+ else:
147
+ print(f"Error: Agent '{alias}' not found", file=sys.stderr)
148
+ sys.exit(1)
149
+
150
+
151
+ def handle_show_config() -> None:
152
+ """Handle --show-config command."""
153
+ print(f"Config file: {CONFIG_FILE}")
154
+
155
+
156
+ def resolve_agent_config(agent_identifier: str) -> dict[str, Any]:
157
+ """Resolve agent identifier to configuration."""
158
+ # Check if it's a saved alias
159
+ saved = get_agent(agent_identifier)
160
+ if saved:
161
+ return saved
162
+
163
+ # Check if it's a URL
164
+ if agent_identifier.startswith(("http://", "https://")):
165
+ return {
166
+ "id": agent_identifier.split("/")[-1],
167
+ "agent_uri": agent_identifier,
168
+ "protocol": "mcp",
169
+ }
170
+
171
+ # Check if it's a JSON config
172
+ if agent_identifier.startswith("{"):
173
+ try:
174
+ return cast(dict[str, Any], json.loads(agent_identifier))
175
+ except json.JSONDecodeError:
176
+ pass
177
+
178
+ print(f"Error: Unknown agent '{agent_identifier}'", file=sys.stderr)
179
+ print(" Not found as saved alias", file=sys.stderr)
180
+ print(" Not a valid URL", file=sys.stderr)
181
+ print(" Not valid JSON config", file=sys.stderr)
182
+ sys.exit(1)
183
+
184
+
185
+ def main() -> None:
186
+ """Main CLI entry point - compatible with JavaScript version."""
187
+ parser = argparse.ArgumentParser(
188
+ description="AdCP Client - Interact with AdCP agents",
189
+ usage="adcp [options] <agent> [tool] [payload]",
190
+ add_help=False,
191
+ )
192
+
193
+ # Configuration management
194
+ parser.add_argument("--save-auth", metavar="ALIAS", help="Save agent configuration")
195
+ parser.add_argument("--list-agents", action="store_true", help="List saved agents")
196
+ parser.add_argument("--remove-agent", metavar="ALIAS", help="Remove saved agent")
197
+ parser.add_argument("--show-config", action="store_true", help="Show config file location")
198
+
199
+ # Execution options
200
+ parser.add_argument("--protocol", choices=["mcp", "a2a"], help="Force protocol type")
201
+ parser.add_argument("--auth", help="Authentication token")
202
+ parser.add_argument("--json", action="store_true", help="Output as JSON")
203
+ parser.add_argument("--debug", action="store_true", help="Enable debug mode")
204
+ parser.add_argument("--help", "-h", action="store_true", help="Show help")
205
+
206
+ # Positional arguments
207
+ parser.add_argument("agent", nargs="?", help="Agent alias, URL, or config")
208
+ parser.add_argument("tool", nargs="?", help="Tool name to execute")
209
+ parser.add_argument("payload", nargs="?", help="Payload (JSON, @file, or stdin)")
210
+
211
+ # Parse known args to handle --save-auth with positional args
212
+ args, remaining = parser.parse_known_args()
213
+
214
+ # Handle help
215
+ if args.help or (
216
+ not args.agent
217
+ and not any(
218
+ [
219
+ args.save_auth,
220
+ args.list_agents,
221
+ args.remove_agent,
222
+ args.show_config,
223
+ ]
224
+ )
225
+ ):
226
+ parser.print_help()
227
+ print("\nExamples:")
228
+ print(" adcp --save-auth myagent https://agent.example.com mcp")
229
+ print(" adcp --list-agents")
230
+ print(" adcp myagent list_tools")
231
+ print(' adcp myagent get_products \'{"brief":"TV ads"}\'')
232
+ print(" adcp https://agent.example.com list_tools")
233
+ sys.exit(0)
234
+
235
+ # Handle configuration commands
236
+ if args.save_auth:
237
+ url = args.agent if args.agent else None
238
+ protocol = args.tool if args.tool else None
239
+ handle_save_auth(args.save_auth, url, protocol)
240
+ sys.exit(0)
241
+
242
+ if args.list_agents:
243
+ handle_list_agents()
244
+ sys.exit(0)
245
+
246
+ if args.remove_agent:
247
+ handle_remove_agent(args.remove_agent)
248
+ sys.exit(0)
249
+
250
+ if args.show_config:
251
+ handle_show_config()
252
+ sys.exit(0)
253
+
254
+ # Execute tool
255
+ if not args.agent:
256
+ print("Error: Agent identifier required", file=sys.stderr)
257
+ sys.exit(1)
258
+
259
+ if not args.tool:
260
+ print("Error: Tool name required", file=sys.stderr)
261
+ sys.exit(1)
262
+
263
+ # Resolve agent config
264
+ agent_config = resolve_agent_config(args.agent)
265
+
266
+ # Override with command-line options
267
+ if args.protocol:
268
+ agent_config["protocol"] = args.protocol
269
+
270
+ if args.auth:
271
+ agent_config["auth_token"] = args.auth
272
+
273
+ if args.debug:
274
+ agent_config["debug"] = True
275
+
276
+ # Load payload
277
+ payload = load_payload(args.payload)
278
+
279
+ # Execute
280
+ asyncio.run(execute_tool(agent_config, args.tool, payload, args.json))
281
+
282
+
283
+ if __name__ == "__main__":
284
+ main()