agentic-fabriq-sdk 0.1.17__tar.gz → 0.1.19__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.

Potentially problematic release.


This version of agentic-fabriq-sdk might be problematic. Click here for more details.

Files changed (44) hide show
  1. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/PKG-INFO +3 -3
  2. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/commands/tools.py +36 -54
  3. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/core/client.py +29 -0
  4. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/main.py +0 -2
  5. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/__init__.py +0 -4
  6. agentic_fabriq_sdk-0.1.19/af_sdk/dx/__init__.py +10 -0
  7. agentic_fabriq_sdk-0.1.19/af_sdk/dx/runtime.py +64 -0
  8. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/events.py +14 -50
  9. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/fabriq_client.py +66 -14
  10. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/models/__init__.py +0 -8
  11. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/models/types.py +0 -50
  12. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/pyproject.toml +3 -3
  13. agentic_fabriq_sdk-0.1.17/af_cli/commands/agents.py +0 -238
  14. agentic_fabriq_sdk-0.1.17/af_sdk/auth/application.py +0 -264
  15. agentic_fabriq_sdk-0.1.17/af_sdk/dx/__init__.py +0 -12
  16. agentic_fabriq_sdk-0.1.17/af_sdk/dx/runtime.py +0 -170
  17. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/README.md +0 -0
  18. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/__init__.py +0 -0
  19. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/commands/__init__.py +0 -0
  20. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/commands/applications.py +0 -0
  21. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/commands/auth.py +0 -0
  22. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/commands/config.py +0 -0
  23. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/commands/mcp_servers.py +0 -0
  24. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/commands/secrets.py +0 -0
  25. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/core/__init__.py +0 -0
  26. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/core/config.py +0 -0
  27. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/core/oauth.py +0 -0
  28. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/core/output.py +0 -0
  29. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/core/token_storage.py +0 -0
  30. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/auth/__init__.py +0 -0
  31. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/auth/applications.py +0 -0
  32. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/auth/dpop.py +0 -0
  33. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/auth/oauth.py +0 -0
  34. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/auth/token_cache.py +0 -0
  35. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/connectors/__init__.py +0 -0
  36. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/connectors/base.py +0 -0
  37. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/connectors/registry.py +0 -0
  38. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/dx/decorators.py +0 -0
  39. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/exceptions.py +0 -0
  40. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/models/audit.py +0 -0
  41. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/py.typed +0 -0
  42. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/transport/__init__.py +0 -0
  43. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/transport/http.py +0 -0
  44. {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/vault.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-fabriq-sdk
3
- Version: 0.1.17
3
+ Version: 0.1.19
4
4
  Summary: Agentic Fabriq SDK: high-level client, CLI tool, DX helpers, and auth for AI agents
5
5
  License: Apache-2.0
6
6
  Keywords: fabriq,agentic-fabriq,sdk,ai,agents,agentic,fabric,cli
@@ -17,7 +17,7 @@ Classifier: Typing :: Typed
17
17
  Requires-Dist: PyJWT (>=2.8.0)
18
18
  Requires-Dist: PyYAML (>=6.0.0)
19
19
  Requires-Dist: aiohttp (>=3.9.0)
20
- Requires-Dist: click (>=8.0.0)
20
+ Requires-Dist: click (>=8.1.0)
21
21
  Requires-Dist: cryptography (>=41.0.0)
22
22
  Requires-Dist: httpx (>=0.25.0)
23
23
  Requires-Dist: keyring (>=25.0.0)
@@ -27,7 +27,7 @@ Requires-Dist: opentelemetry-instrumentation-httpx (>=0.41b0)
27
27
  Requires-Dist: pydantic (>=2.4.0)
28
28
  Requires-Dist: rich (>=13.7.0)
29
29
  Requires-Dist: stevedore (>=5.1.0)
30
- Requires-Dist: typer[all] (>=0.12.0)
30
+ Requires-Dist: typer[all] (>=0.20.0)
31
31
  Requires-Dist: typing-extensions (>=4.0.0)
32
32
  Project-URL: Documentation, https://docs.agentic-fabric.org
33
33
  Project-URL: Homepage, https://github.com/agentic-fabric/agentic-fabric
@@ -121,17 +121,20 @@ def get(
121
121
 
122
122
  @app.command()
123
123
  def invoke(
124
- tool_identifier: str = typer.Argument(..., help="Tool name (e.g., 'slack') or UUID"),
124
+ connection_id: str = typer.Argument(..., help="Connection ID (e.g., 'slacker', 'gurt')"),
125
125
  method: str = typer.Option(..., "--method", help="Tool method to invoke"),
126
126
  params: str = typer.Option(None, "--params", help="JSON string of method parameters (e.g., '{\"channel\": \"test\", \"text\": \"Hello\"}')"),
127
127
  format: str = typer.Option("json", "--format", help="Output format (json, table, yaml)"),
128
128
  ):
129
- """Invoke a tool by name or UUID.
129
+ """Invoke a tool using its connection ID.
130
+
131
+ The connection ID identifies which specific tool connection to use.
132
+ Run 'afctl tools list' to see your connection IDs.
130
133
 
131
134
  Examples:
132
- afctl tools invoke slack --method get_channels
133
- afctl tools invoke slack --method post_message --params '{"channel": "test", "text": "Hello!"}'
134
- afctl tools invoke c17b55af-18f8-49be-b9e9-44b1d1be429e --method get_channels
135
+ afctl tools invoke slacker --method get_channels
136
+ afctl tools invoke slacker --method post_message --params '{"channel": "test", "text": "Hello!"}'
137
+ afctl tools invoke gurt --method list_files
135
138
  """
136
139
  try:
137
140
  # Parse parameters if provided
@@ -145,59 +148,29 @@ def invoke(
145
148
  raise typer.Exit(1)
146
149
 
147
150
  with get_client() as client:
148
- # Try to resolve tool name to UUID if not already a UUID
149
- tool_id = tool_identifier
150
- try:
151
- from uuid import UUID
152
- UUID(tool_identifier)
153
- # It's already a UUID, use it directly
154
- except ValueError:
155
- # Not a UUID, try to look up by name
156
- info(f"Looking up tool '{tool_identifier}'...")
157
- try:
158
- tools_response = client.get("/api/v1/tools")
159
-
160
- # Handle different response formats
161
- if isinstance(tools_response, dict) and "tools" in tools_response:
162
- tools = tools_response["tools"]
163
- elif isinstance(tools_response, dict) and "items" in tools_response:
164
- tools = tools_response["items"]
165
- elif hasattr(tools_response, '__iter__') and not isinstance(tools_response, (str, dict)):
166
- tools = list(tools_response)
167
- else:
168
- tools = []
169
-
170
- # Find tool by name (case-insensitive)
171
- matching_tools = [t for t in tools if isinstance(t, dict) and t.get("name", "").lower() == tool_identifier.lower()]
172
-
173
- if not matching_tools:
174
- error(f"Tool '{tool_identifier}' not found. Available tools:")
175
- for t in tools:
176
- if isinstance(t, dict):
177
- print(f" - {t.get('name')} (ID: {t.get('id')})")
178
- raise typer.Exit(1)
179
-
180
- if len(matching_tools) > 1:
181
- error(f"Multiple tools found with name '{tool_identifier}':")
182
- for t in matching_tools:
183
- print(f" - {t.get('name')} (ID: {t.get('id')})")
184
- error("Please use the UUID instead.")
185
- raise typer.Exit(1)
186
-
187
- tool_id = matching_tools[0].get("id")
188
- info(f"Resolved '{tool_identifier}' to tool ID: {tool_id}")
189
- except Exception as lookup_error:
190
- error(f"Failed to look up tool: {lookup_error}")
191
- raise typer.Exit(1)
151
+ info(f"Invoking connection '{connection_id}' with method '{method}'...")
192
152
 
153
+ # Verify connection exists
154
+ connections = client.get("/api/v1/user-connections")
155
+ connection = next((c for c in connections if c.get("connection_id") == connection_id), None)
156
+
157
+ if not connection:
158
+ error(f"Connection '{connection_id}' not found")
159
+ info("Available connections:")
160
+ for conn in connections:
161
+ info(f" - {conn.get('connection_id')} ({conn.get('tool')})")
162
+ raise typer.Exit(1)
163
+
164
+ tool_name = connection.get("tool")
165
+ debug(f"Connection '{connection_id}' uses tool '{tool_name}'")
166
+
167
+ # Use the connection-based invoke endpoint (auto-creates tool if needed)
193
168
  data = {
194
169
  "method": method,
195
170
  "parameters": parameters,
196
- "context": {},
197
171
  }
198
172
 
199
- info(f"Invoking tool {tool_identifier} method {method}...")
200
- response = client.post(f"/api/v1/tools/{tool_id}/invoke", data)
173
+ response = client.post(f"/api/v1/tools/connections/{connection_id}/invoke", data)
201
174
 
202
175
  success("Tool invoked successfully")
203
176
 
@@ -349,8 +322,11 @@ def add(
349
322
  if redirect_uri:
350
323
  config_payload["redirect_uri"] = redirect_uri
351
324
 
325
+ # For Google tools, pass tool_type parameter to prevent duplicates
326
+ tool_type_param = f"&tool_type={tool}" if api_tool_name == "google" else ""
327
+
352
328
  client.post(
353
- f"/api/v1/tools/{api_tool_name}/config?connection_id={connection_id}",
329
+ f"/api/v1/tools/{api_tool_name}/config?connection_id={connection_id}{tool_type_param}",
354
330
  data=config_payload
355
331
  )
356
332
  success("✅ OAuth app credentials stored")
@@ -523,9 +499,15 @@ def disconnect(
523
499
  info("Cancelled")
524
500
  return
525
501
 
502
+ # Determine the API base tool name (Google tools all use "google")
503
+ api_tool_name = "google" if (tool.startswith("google_") or tool == "gmail") else tool
504
+
505
+ # For Google tools, pass tool_type parameter
506
+ tool_type_param = f"&tool_type={tool}" if api_tool_name == "google" else ""
507
+
526
508
  # Delete connection credentials
527
509
  client.delete(
528
- f"/api/v1/tools/{tool}/connection?connection_id={connection_id}"
510
+ f"/api/v1/tools/{api_tool_name}/connection?connection_id={connection_id}{tool_type_param}"
529
511
  )
530
512
 
531
513
  success(f"✅ Disconnected: {connection_id}")
@@ -86,6 +86,35 @@ class AFClient:
86
86
 
87
87
  return self._handle_response(response)
88
88
 
89
+ def try_post(self, path: str, data: Optional[Dict] = None) -> tuple[bool, int, Optional[Dict[str, Any]]]:
90
+ """Make POST request without exiting on error. Returns (success, status_code, response_data)."""
91
+ url = urljoin(self.config.gateway_url, path)
92
+ debug(f"POST {url}")
93
+
94
+ try:
95
+ response = self.client.post(
96
+ path,
97
+ json=data,
98
+ headers=self._get_headers(),
99
+ )
100
+
101
+ debug(f"Response: {response.status_code} {response.url}")
102
+
103
+ if response.status_code >= 400:
104
+ try:
105
+ error_data = response.json()
106
+ return False, response.status_code, error_data
107
+ except:
108
+ return False, response.status_code, {"detail": response.text}
109
+
110
+ try:
111
+ return True, response.status_code, response.json()
112
+ except:
113
+ return True, response.status_code, {"message": "Success"}
114
+
115
+ except Exception as e:
116
+ return False, 0, {"detail": str(e)}
117
+
89
118
  def put(self, path: str, data: Optional[Dict] = None) -> Dict[str, Any]:
90
119
  """Make PUT request."""
91
120
  url = urljoin(self.config.gateway_url, path)
@@ -9,7 +9,6 @@ import typer
9
9
  from rich.console import Console
10
10
  from rich.table import Table
11
11
 
12
- from af_cli.commands.agents import app as agents_app
13
12
  from af_cli.commands.applications import app as applications_app
14
13
  from af_cli.commands.auth import app as auth_app
15
14
  from af_cli.commands.config import app as config_app
@@ -30,7 +29,6 @@ console = Console()
30
29
  # Add subcommands
31
30
  app.add_typer(auth_app, name="auth", help="Authentication commands")
32
31
  app.add_typer(config_app, name="config", help="Configuration commands")
33
- app.add_typer(agents_app, name="agents", help="Agent management commands")
34
32
  app.add_typer(tools_app, name="tools", help="Tool management commands")
35
33
  app.add_typer(applications_app, name="applications", help="Application management commands")
36
34
  app.add_typer(mcp_servers_app, name="mcp-servers", help="MCP server management commands")
@@ -15,8 +15,6 @@ from .exceptions import (
15
15
  ValidationError,
16
16
  )
17
17
  from .models.types import (
18
- AgentInvokeRequest,
19
- AgentInvokeResult,
20
18
  ToolInvokeRequest,
21
19
  ToolInvokeResult,
22
20
  )
@@ -45,8 +43,6 @@ __all__ = [
45
43
  "ConnectorError",
46
44
  "NotFoundError",
47
45
  "ValidationError",
48
- "AgentInvokeRequest",
49
- "AgentInvokeResult",
50
46
  "ToolInvokeRequest",
51
47
  "ToolInvokeResult",
52
48
  "HTTPClient",
@@ -0,0 +1,10 @@
1
+ from .decorators import tool
2
+ from .runtime import ToolFabric, MCPServer
3
+
4
+ __all__ = [
5
+ "tool",
6
+ "ToolFabric",
7
+ "MCPServer",
8
+ ]
9
+
10
+
@@ -0,0 +1,64 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Dict, List, Optional
4
+ import os
5
+ import httpx
6
+
7
+
8
+ def _base_headers(token: str, tenant_id: Optional[str]) -> Dict[str, str]:
9
+ headers = {"Authorization": f"Bearer {token}"}
10
+ if tenant_id:
11
+ headers["X-Tenant-Id"] = tenant_id
12
+ # Dev helper: allow overriding scopes from env for local testing
13
+ debug_scopes = os.getenv("FABRIQ_DEBUG_SCOPES")
14
+ if debug_scopes is not None:
15
+ headers["X-Debug-Scopes"] = debug_scopes
16
+ return headers
17
+
18
+
19
+ class ToolFabric:
20
+ """Thin facade over Fabriq provider proxy endpoints (e.g., Slack).
21
+
22
+ This class lets developers think in terms of a "fabric" of tools provided
23
+ by a vendor, while under the hood we call the Gateway proxy endpoints.
24
+ """
25
+
26
+ def __init__(self, *, provider: str, base_url: str, access_token: str, tenant_id: Optional[str] = None):
27
+ self.provider = provider
28
+ self.base_url = base_url.rstrip("/")
29
+ self.token = access_token
30
+ self.tenant_id = tenant_id
31
+
32
+ def get_tools(self, names: List[str]) -> List[str]:
33
+ # Placeholder: simply returns opaque method identifiers as strings the Agent understands
34
+ return [f"{self.provider}:{name}" for name in names]
35
+
36
+ def invoke(self, action: str, params: Dict[str, Any]) -> Dict[str, Any]:
37
+ url = f"{self.base_url}/api/v1/proxy/{self.provider}/{action}"
38
+ with httpx.Client(timeout=30.0) as c:
39
+ r = c.post(url, json=params, headers=_base_headers(self.token, self.tenant_id))
40
+ r.raise_for_status()
41
+ return r.json()
42
+
43
+
44
+ class MCPServer:
45
+ """Facade for an MCP server registered in Fabriq (proxy layer)."""
46
+
47
+ def __init__(self, *, server_id: str, base_url: str, access_token: str, tenant_id: Optional[str] = None):
48
+ self.server_id = server_id
49
+ self.base_url = base_url.rstrip("/")
50
+ self.token = access_token
51
+ self.tenant_id = tenant_id
52
+
53
+ def get_tools(self, names: List[str]) -> List[str]:
54
+ return [f"mcp:{self.server_id}:{name}" for name in names]
55
+
56
+ def invoke(self, tool: str, args: Dict[str, Any]) -> Dict[str, Any]:
57
+ url = f"{self.base_url}/api/v1/proxy/mcp/{self.server_id}/invoke"
58
+ payload = {"payload": {"tool": tool, "args": args}}
59
+ with httpx.Client(timeout=60.0) as c:
60
+ r = c.post(url, json=payload, headers=_base_headers(self.token, self.tenant_id))
61
+ r.raise_for_status()
62
+ return r.json()
63
+
64
+
@@ -35,17 +35,6 @@ class EventMetadata(BaseModel):
35
35
  trace_id: Optional[str] = None
36
36
 
37
37
 
38
- class AgentEvent(BaseModel):
39
- """Agent lifecycle event"""
40
- event_type: str # registered, updated, deleted, invoked
41
- agent_id: str
42
- agent_name: str
43
- agent_type: str
44
- tenant_id: str
45
- metadata: EventMetadata
46
- payload: Dict[str, Any] = Field(default_factory=dict)
47
-
48
-
49
38
  class ToolEvent(BaseModel):
50
39
  """Tool lifecycle event"""
51
40
  event_type: str # registered, updated, deleted, invoked
@@ -81,7 +70,7 @@ class SecretEvent(BaseModel):
81
70
  payload: Dict[str, Any] = Field(default_factory=dict)
82
71
 
83
72
 
84
- Event = Union[AgentEvent, ToolEvent, InvocationEvent, SecretEvent]
73
+ Event = Union[ToolEvent, InvocationEvent, SecretEvent]
85
74
 
86
75
 
87
76
  class EventStreamConfig(BaseModel):
@@ -127,25 +116,10 @@ class EventPublisher:
127
116
  payload: Optional[Dict] = None,
128
117
  correlation_id: Optional[str] = None
129
118
  ):
130
- """Publish agent lifecycle event"""
131
- metadata = EventMetadata(
132
- source=source,
133
- correlation_id=correlation_id,
134
- tenant_id=tenant_id
135
- )
136
-
137
- event = AgentEvent(
138
- event_type=event_type,
139
- agent_id=agent_id,
140
- agent_name=agent_name,
141
- agent_type=agent_type,
142
- tenant_id=tenant_id,
143
- metadata=metadata,
144
- payload=payload or {}
145
- )
146
-
147
- subject = f"agents.{event_type}.{tenant_id}"
148
- await self._publish_event(subject, event)
119
+ """Publish agent lifecycle event - DEPRECATED"""
120
+ # Agent events have been removed from the SDK
121
+ # This method is kept for backward compatibility but does nothing
122
+ pass
149
123
 
150
124
  async def publish_tool_event(
151
125
  self,
@@ -284,19 +258,15 @@ class EventSubscriber:
284
258
 
285
259
  async def subscribe_to_agent_events(
286
260
  self,
287
- handler: Callable[[AgentEvent], Awaitable[None]],
261
+ handler: Callable,
288
262
  tenant_id: Optional[str] = None,
289
263
  event_type: Optional[str] = None,
290
264
  consumer_name: str = "agent-events-consumer"
291
265
  ):
292
- """Subscribe to agent events"""
293
- subject = "agents.*"
294
- if tenant_id:
295
- subject += f".{tenant_id}"
296
- if event_type:
297
- subject = f"agents.{event_type}.*"
298
-
299
- await self._subscribe(subject, handler, consumer_name, AgentEvent)
266
+ """Subscribe to agent events - DEPRECATED"""
267
+ # Agent events have been removed from the SDK
268
+ # This method is kept for backward compatibility but does nothing
269
+ pass
300
270
 
301
271
  async def subscribe_to_tool_events(
302
272
  self,
@@ -641,16 +611,10 @@ async def publish_agent_registered(
641
611
  tenant_id: str,
642
612
  payload: Optional[Dict] = None
643
613
  ):
644
- """Convenience function to publish agent registered event"""
645
- await publisher.publish_agent_event(
646
- event_type="registered",
647
- agent_id=agent_id,
648
- agent_name=agent_name,
649
- agent_type=agent_type,
650
- tenant_id=tenant_id,
651
- source="af_gateway",
652
- payload=payload
653
- )
614
+ """Convenience function to publish agent registered event - DEPRECATED"""
615
+ # Agent events have been removed from the SDK
616
+ # This function is kept for backward compatibility but does nothing
617
+ pass
654
618
 
655
619
 
656
620
  async def publish_invocation_started(
@@ -85,6 +85,7 @@ class FabriqClient:
85
85
  method: str,
86
86
  parameters: Optional[Dict[str, Any]] = None,
87
87
  context: Optional[Dict[str, Any]] = None,
88
+ connection_id: Optional[str] = None,
88
89
  ) -> Dict[str, Any]:
89
90
  """Invoke a tool by name or UUID.
90
91
 
@@ -93,6 +94,7 @@ class FabriqClient:
93
94
  method: Method name to invoke
94
95
  parameters: Method parameters
95
96
  context: Additional context
97
+ connection_id: Specific connection ID to use (for multi-connection tools)
96
98
 
97
99
  Returns:
98
100
  Tool invocation result
@@ -101,6 +103,8 @@ class FabriqClient:
101
103
  result = await client.invoke_tool("slack", method="get_channels")
102
104
  result = await client.invoke_tool("slack", method="post_message",
103
105
  parameters={"channel": "test", "text": "Hello!"})
106
+ result = await client.invoke_tool("slack", method="get_channels",
107
+ connection_id="slacker")
104
108
  """
105
109
  # Try to resolve tool name to UUID if not already a UUID
106
110
  tool_id = tool_identifier
@@ -142,10 +146,70 @@ class FabriqClient:
142
146
  body = {"method": method}
143
147
  if parameters is not None:
144
148
  body["parameters"] = parameters
145
- if context is not None:
146
- body["context"] = context
149
+
150
+ # Add connection_id to context if provided
151
+ if context is not None or connection_id is not None:
152
+ ctx = (context or {}).copy()
153
+ if connection_id is not None:
154
+ ctx["connection_id"] = connection_id
155
+ body["context"] = ctx
156
+
147
157
  r = await self._http.post(f"/tools/{tool_id}/invoke", json=body, headers=self._extra_headers)
148
158
  return r.json()
159
+
160
+ async def invoke_connection(
161
+ self,
162
+ connection_id: str,
163
+ *,
164
+ method: str,
165
+ parameters: Optional[Dict[str, Any]] = None,
166
+ ) -> Dict[str, Any]:
167
+ """Invoke a tool using its connection ID (preferred method).
168
+
169
+ This uses the connection_id (like 'slacker', 'gurt') to automatically
170
+ find and invoke the correct tool with the right credentials.
171
+
172
+ Args:
173
+ connection_id: Connection identifier (e.g., 'slacker', 'gurt')
174
+ method: Method name to invoke
175
+ parameters: Method parameters
176
+
177
+ Returns:
178
+ Tool invocation result
179
+
180
+ Examples:
181
+ result = await client.invoke_connection("slacker", method="get_channels")
182
+ result = await client.invoke_connection("slacker", method="post_message",
183
+ parameters={"channel": "test", "text": "Hello!"})
184
+ result = await client.invoke_connection("gurt", method="list_files")
185
+ """
186
+ # This uses the tool invocation with connection_id in context
187
+ # which is supported by current production servers
188
+ body = {
189
+ "method": method,
190
+ "parameters": parameters or {},
191
+ "context": {"connection_id": connection_id}
192
+ }
193
+
194
+ # First, get the connection to find the tool name
195
+ connections_resp = await self._http.get("/user-connections", headers=self._extra_headers)
196
+ connections = connections_resp.json()
197
+
198
+ connection = next((c for c in connections if c.get("connection_id") == connection_id), None)
199
+ if not connection:
200
+ raise ValueError(f"Connection '{connection_id}' not found")
201
+
202
+ tool_name = connection.get("tool")
203
+
204
+ # Look up the tool by name to get its UUID
205
+ result = await self.invoke_tool(
206
+ tool_name,
207
+ method=method,
208
+ parameters=parameters,
209
+ connection_id=connection_id
210
+ )
211
+
212
+ return result
149
213
 
150
214
  # -----------------
151
215
  # MCP Servers
@@ -184,18 +248,6 @@ class FabriqClient:
184
248
  r = await self._http.post(f"/proxy/mcp/{server_id}/invoke", json={"payload": payload, "raw": raw}, headers=self._extra_headers)
185
249
  return r.json()
186
250
 
187
- # -----------------
188
- # Agents
189
- # -----------------
190
- async def list_agents(self, *, page: int = 1, page_size: int = 20, sort_by: str = "name", sort_order: str = "asc") -> Dict[str, Any]:
191
- params: Dict[str, Any] = {"page": page, "page_size": page_size, "sort_by": sort_by, "sort_order": sort_order}
192
- r = await self._http.get("/agents", params=params, headers=self._extra_headers)
193
- return r.json()
194
-
195
- async def invoke_agent(self, *, agent_id: str, params: Dict[str, Any], raw: bool = False) -> Dict[str, Any]:
196
- r = await self._http.post(f"/proxy/agents/{agent_id}/invoke", json={"params": params, "raw": raw}, headers=self._extra_headers)
197
- return r.json()
198
-
199
251
  # -----------------
200
252
  # Secrets (Gateway-backed Vault)
201
253
  # -----------------
@@ -3,10 +3,6 @@ Data models for Agentic Fabric SDK.
3
3
  """
4
4
 
5
5
  from .types import (
6
- Agent,
7
- AgentCreate,
8
- AgentInvokeRequest,
9
- AgentInvokeResult,
10
6
  ErrorResponse,
11
7
  HealthResponse,
12
8
  McpServer,
@@ -25,10 +21,6 @@ from .types import (
25
21
  )
26
22
 
27
23
  __all__ = [
28
- "Agent",
29
- "AgentCreate",
30
- "AgentInvokeRequest",
31
- "AgentInvokeResult",
32
24
  "Tool",
33
25
  "ToolInvokeRequest",
34
26
  "ToolInvokeResult",
@@ -9,56 +9,6 @@ from uuid import UUID
9
9
  from pydantic import BaseModel, Field
10
10
 
11
11
 
12
- class Agent(BaseModel):
13
- """Agent model."""
14
-
15
- id: str
16
- name: str
17
- description: Optional[str] = None
18
- owner: str
19
- tenant_id: str
20
- group_path: Optional[str] = None
21
- endpoint_url: Optional[str] = None
22
- auth_type: Optional[str] = None
23
- scopes: List[str] = Field(default_factory=list)
24
- metadata: Dict[str, Any] = Field(default_factory=dict)
25
- created_at: datetime
26
- updated_at: datetime
27
-
28
-
29
- class AgentCreate(BaseModel):
30
- """Request model for creating an agent."""
31
-
32
- id: str
33
- name: str
34
- description: Optional[str] = None
35
- endpoint_url: Optional[str] = None
36
- auth_type: Optional[str] = None
37
- scopes: List[str] = Field(default_factory=list)
38
- metadata: Dict[str, Any] = Field(default_factory=dict)
39
- group_path: Optional[str] = None
40
-
41
-
42
- class AgentInvokeRequest(BaseModel):
43
- """Request model for invoking an agent."""
44
-
45
- input: str
46
- params: Dict[str, Any] = Field(default_factory=dict)
47
- max_tokens: Optional[int] = None
48
- temperature: Optional[float] = None
49
- stream: bool = False
50
-
51
-
52
- class AgentInvokeResult(BaseModel):
53
- """Result model for agent invocation."""
54
-
55
- output: str
56
- logs: List[str] = Field(default_factory=list)
57
- usage: Optional[Dict[str, Any]] = None
58
- metadata: Dict[str, Any] = Field(default_factory=dict)
59
- status: str = "success"
60
-
61
-
62
12
  class Tool(BaseModel):
63
13
  """Tool model."""
64
14
 
@@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
5
5
 
6
6
  [tool.poetry]
7
7
  name = "agentic-fabriq-sdk"
8
- version = "0.1.17"
8
+ version = "0.1.19"
9
9
  description = "Agentic Fabriq SDK: high-level client, CLI tool, DX helpers, and auth for AI agents"
10
10
  readme = "README.md"
11
11
  license = "Apache-2.0"
@@ -43,11 +43,11 @@ aiohttp = ">=3.9.0"
43
43
  # Plugin/extension system
44
44
  stevedore = ">=5.1.0"
45
45
  # CLI dependencies
46
- typer = {extras = ["all"], version = ">=0.12.0"}
46
+ typer = {extras = ["all"], version = ">=0.20.0"}
47
47
  rich = ">=13.7.0"
48
48
  keyring = ">=25.0.0"
49
49
  cryptography = ">=41.0.0"
50
- click = ">=8.0.0"
50
+ click = ">=8.1.0"
51
51
  PyYAML = ">=6.0.0"
52
52
 
53
53
  [tool.poetry.scripts]