agentic-fabriq-sdk 0.1.24__tar.gz → 0.1.25__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 (41) hide show
  1. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/PKG-INFO +1 -1
  2. agentic_fabriq_sdk-0.1.25/af_cli/__init__.py +17 -0
  3. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_cli/commands/applications.py +40 -5
  4. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_cli/commands/auth.py +8 -7
  5. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_cli/commands/config.py +37 -11
  6. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_cli/commands/tools.py +47 -16
  7. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_cli/core/client.py +6 -1
  8. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_cli/core/config.py +2 -1
  9. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_cli/main.py +6 -10
  10. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/connectors/__init__.py +0 -2
  11. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/connectors/base.py +1 -42
  12. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/dx/__init__.py +1 -2
  13. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/dx/runtime.py +0 -22
  14. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/fabriq_client.py +0 -38
  15. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/models/__init__.py +0 -4
  16. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/models/types.py +0 -22
  17. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/pyproject.toml +1 -1
  18. agentic_fabriq_sdk-0.1.24/af_cli/__init__.py +0 -8
  19. agentic_fabriq_sdk-0.1.24/af_cli/commands/mcp_servers.py +0 -83
  20. agentic_fabriq_sdk-0.1.24/af_cli/commands/secrets.py +0 -109
  21. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/README.md +0 -0
  22. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_cli/commands/__init__.py +0 -0
  23. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_cli/core/__init__.py +0 -0
  24. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_cli/core/oauth.py +0 -0
  25. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_cli/core/output.py +0 -0
  26. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_cli/core/token_storage.py +0 -0
  27. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/__init__.py +0 -0
  28. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/auth/__init__.py +0 -0
  29. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/auth/applications.py +0 -0
  30. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/auth/dpop.py +0 -0
  31. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/auth/oauth.py +0 -0
  32. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/auth/token_cache.py +0 -0
  33. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/connectors/registry.py +0 -0
  34. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/dx/decorators.py +0 -0
  35. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/events.py +0 -0
  36. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/exceptions.py +0 -0
  37. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/models/audit.py +0 -0
  38. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/py.typed +0 -0
  39. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/transport/__init__.py +0 -0
  40. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/af_sdk/transport/http.py +0 -0
  41. {agentic_fabriq_sdk-0.1.24 → agentic_fabriq_sdk-0.1.25}/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.24
3
+ Version: 0.1.25
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
@@ -0,0 +1,17 @@
1
+ """
2
+ Agentic Fabric CLI Tool
3
+
4
+ A command-line interface for managing Agentic Fabric resources including
5
+ agents, tools, and administrative operations.
6
+ """
7
+
8
+ try:
9
+ from importlib.metadata import version, PackageNotFoundError
10
+ try:
11
+ __version__ = version("agentic-fabriq-sdk")
12
+ except PackageNotFoundError:
13
+ # Fallback if package is not installed
14
+ __version__ = "0.0.0-dev"
15
+ except ImportError:
16
+ # Fallback for Python < 3.8 (though we require 3.11+)
17
+ __version__ = "0.0.0-dev"
@@ -190,24 +190,59 @@ def connect_application(
190
190
  @app.command("list")
191
191
  def list_applications(
192
192
  format: str = typer.Option("table", "--format", help="Output format (table, json, yaml)"),
193
+ sync: bool = typer.Option(True, "--sync/--no-sync", help="Sync with server and remove orphaned local files"),
193
194
  ):
194
195
  """
195
196
  List all registered applications.
196
197
 
197
- Shows applications from both:
198
- - Local config files (~/.af/applications/)
199
- - Server (via API)
198
+ Shows applications from local config files (~/.af/applications/) and optionally
199
+ syncs with the server to remove any local files for applications that have been
200
+ deleted from the server (e.g., via the UI).
200
201
  """
201
202
  config = get_config()
202
203
 
203
204
  # Load from local config first
204
- from af_sdk import list_applications as list_local_apps
205
+ from af_sdk import list_applications as list_local_apps, delete_application_config
205
206
 
206
207
  local_apps = list_local_apps()
207
208
 
209
+ # If sync is enabled and user is authenticated, check server and clean up orphans
210
+ if sync and config.is_authenticated():
211
+ try:
212
+ response = httpx.get(
213
+ f"{config.gateway_url}/api/v1/applications",
214
+ headers={"Authorization": f"Bearer {config.access_token}"},
215
+ timeout=30.0
216
+ )
217
+
218
+ if response.status_code == 200:
219
+ data = response.json()
220
+ server_apps = data.get("applications", [])
221
+ server_app_ids = {app["app_id"] for app in server_apps}
222
+
223
+ # Find and remove orphaned local files
224
+ orphaned = []
225
+ for local_app in local_apps[:]: # Copy list to modify during iteration
226
+ if local_app["app_id"] not in server_app_ids:
227
+ orphaned.append(local_app["app_id"])
228
+ # Delete orphaned local config
229
+ delete_application_config(local_app["app_id"])
230
+ # Remove from local_apps list
231
+ local_apps.remove(local_app)
232
+
233
+ if orphaned:
234
+ console.print(f"🧹 Cleaned up {len(orphaned)} orphaned local file(s): {', '.join(orphaned)}", style="yellow")
235
+
236
+ except httpx.HTTPError as e:
237
+ # If server check fails, just show local apps with a warning
238
+ console.print(f"⚠️ Could not sync with server: {e}", style="yellow")
239
+ except Exception as e:
240
+ # Silently continue if sync fails
241
+ pass
242
+
208
243
  if format == "table":
209
244
  if not local_apps:
210
- console.print("No applications registered locally.", style="yellow")
245
+ console.print("No applications registered.", style="yellow")
211
246
  return
212
247
 
213
248
  table = Table(title="Registered Applications")
@@ -53,15 +53,16 @@ def login(
53
53
  "--tenant-id",
54
54
  help="Tenant ID (optional, can be extracted from JWT)"
55
55
  ),
56
- browser: bool = typer.Option(
57
- True,
58
- help="Open browser for authentication"
59
- ),
60
56
  keycloak_url: Optional[str] = typer.Option(
61
57
  None,
62
58
  "--keycloak-url",
63
59
  help="Keycloak URL (default: https://auth.agenticfabriq.com or from config)"
64
60
  ),
61
+ yes: bool = typer.Option(
62
+ False,
63
+ "--yes",
64
+ help="Skip confirmation when already authenticated"
65
+ ),
65
66
  ):
66
67
  """
67
68
  Login to Agentic Fabric using OAuth2/PKCE flow.
@@ -80,7 +81,7 @@ def login(
80
81
  info(f"Already authenticated as: {user_display}")
81
82
  info(f"Tenant: {existing_token.tenant_id or 'Unknown'}")
82
83
 
83
- if not typer.confirm("Do you want to login again?"):
84
+ if not yes and not typer.confirm("Do you want to login again?"):
84
85
  return
85
86
 
86
87
  try:
@@ -89,8 +90,8 @@ def login(
89
90
 
90
91
  # Perform login
91
92
  console.print()
92
- # Use direct localhost redirect until branded page is deployed
93
- tokens = oauth_client.login(open_browser=browser, timeout=300, use_hosted_callback=False)
93
+ # Always open browser for authentication
94
+ tokens = oauth_client.login(open_browser=True, timeout=300, use_hosted_callback=False)
94
95
 
95
96
  # Extract and save token data
96
97
  token_data = token_storage.extract_token_info(tokens)
@@ -12,12 +12,14 @@ app = typer.Typer(help="Configuration commands")
12
12
 
13
13
  @app.command()
14
14
  def show(
15
- format: str = typer.Option("table", "--format", help="Output format"),
15
+ format: str = typer.Option(None, "--format", help="Output format (overrides configured default)"),
16
16
  ):
17
17
  """Show current configuration."""
18
- import os
19
18
  config = get_config()
20
19
 
20
+ # Use provided format, or fall back to configured output_format
21
+ display_format = format if format else config.output_format
22
+
21
23
  config_data = {
22
24
  "gateway_url": config.gateway_url,
23
25
  "keycloak_url": config.keycloak_url,
@@ -25,13 +27,12 @@ def show(
25
27
  "authenticated": "Yes" if config.is_authenticated() else "No",
26
28
  "config_file": config.config_file,
27
29
  "output_format": config.output_format,
28
- "env_gateway_url": os.getenv("AF_GATEWAY_URL", "Not set"),
29
- "env_keycloak_url": os.getenv("AF_KEYCLOAK_URL", "Not set"),
30
+ "page_size": config.page_size,
30
31
  }
31
32
 
32
33
  print_output(
33
34
  config_data,
34
- format_type=format,
35
+ format_type=display_format,
35
36
  title="Configuration"
36
37
  )
37
38
 
@@ -44,15 +45,20 @@ def set(
44
45
  """Set configuration value."""
45
46
  config = get_config()
46
47
 
48
+ # Note: tenant_id is not settable - it comes from authentication
47
49
  valid_keys = {
48
50
  "gateway_url": "gateway_url",
49
- "tenant_id": "tenant_id",
51
+ "keycloak_url": "keycloak_url",
50
52
  "output_format": "output_format",
53
+ "page_size": "page_size",
51
54
  }
52
55
 
53
56
  if key not in valid_keys:
54
57
  error(f"Invalid configuration key: {key}")
55
58
  error(f"Valid keys: {', '.join(valid_keys.keys())}")
59
+ if key == "tenant_id":
60
+ error("Note: tenant_id cannot be set manually. It comes from authentication.")
61
+ error("Run 'afctl auth login' to authenticate with a specific tenant.")
56
62
  raise typer.Exit(1)
57
63
 
58
64
  # Set the value
@@ -69,27 +75,46 @@ def get(
69
75
  """Get configuration value."""
70
76
  config = get_config()
71
77
 
78
+ # All readable config keys
72
79
  valid_keys = {
73
80
  "gateway_url": "gateway_url",
81
+ "keycloak_url": "keycloak_url",
82
+ "keycloak_realm": "keycloak_realm",
83
+ "keycloak_client_id": "keycloak_client_id",
74
84
  "tenant_id": "tenant_id",
75
85
  "output_format": "output_format",
86
+ "page_size": "page_size",
87
+ "config_file": "config_file",
88
+ "verbose": "verbose",
89
+ "authenticated": "is_authenticated", # Special: calls method
76
90
  }
77
91
 
78
92
  if key not in valid_keys:
79
93
  error(f"Invalid configuration key: {key}")
80
- error(f"Valid keys: {', '.join(valid_keys.keys())}")
94
+ error(f"Valid keys: {', '.join(sorted(valid_keys.keys()))}")
81
95
  raise typer.Exit(1)
82
96
 
83
- value = getattr(config, valid_keys[key])
84
- info(f"{key}: {value}")
97
+ # Handle special keys that are methods
98
+ if key == "authenticated":
99
+ value = "Yes" if config.is_authenticated() else "No"
100
+ else:
101
+ value = getattr(config, valid_keys[key])
102
+
103
+ # Display value
104
+ if value is None:
105
+ info(f"{key}: (not set)")
106
+ else:
107
+ info(f"{key}: {value}")
85
108
 
86
109
 
87
110
  @app.command()
88
- def reset():
111
+ def reset(
112
+ yes: bool = typer.Option(False, "--yes", help="Skip confirmation"),
113
+ ):
89
114
  """Reset configuration to defaults."""
90
115
  config = get_config()
91
116
 
92
- if not typer.confirm("Are you sure you want to reset configuration to defaults?"):
117
+ if not yes and not typer.confirm("Are you sure you want to reset configuration to defaults?"):
93
118
  info("Reset cancelled")
94
119
  return
95
120
 
@@ -100,6 +125,7 @@ def reset():
100
125
  config.gateway_url = "https://dashboard.agenticfabriq.com"
101
126
  config.tenant_id = None
102
127
  config.output_format = "table"
128
+ config.page_size = 20
103
129
 
104
130
  config.save()
105
131
 
@@ -15,7 +15,7 @@ app = typer.Typer(help="Tool management commands")
15
15
  def list(
16
16
  format: str = typer.Option("table", "--format", help="Output format"),
17
17
  page: int = typer.Option(1, "--page", help="Page number (starts from 1)", min=1),
18
- page_size: int = typer.Option(20, "--page-size", help="Number of items per page (1-100)", min=1, max=100),
18
+ page_size: int = typer.Option(None, "--page-size", help="Number of items per page (1-100). When specified, becomes the new default.", min=1, max=100),
19
19
  search: str = typer.Option(None, "--search", help="Search query (searches tool IDs from registry like 'gmail', 'slack', and user connection names)"),
20
20
  tool_filter: str = typer.Option(None, "--tool", help="Filter by tool type from registry (e.g., 'gmail' shows Gmail connections, 'google' shows all Google tools)"),
21
21
  ):
@@ -75,22 +75,41 @@ def list(
75
75
  - Combines with search and pagination
76
76
  """
77
77
  try:
78
+ from af_cli.core.config import get_config
79
+ config = get_config()
80
+
81
+ # Use provided page_size, or fall back to configured default
82
+ if page_size is None:
83
+ page_size = config.page_size
84
+ else:
85
+ # Save the new page_size as default
86
+ config.page_size = page_size
87
+ config.save()
88
+
78
89
  with get_client() as client:
79
- # Build query parameters
80
- params = {}
81
- if page != 1:
82
- params["page"] = page
83
- if page_size != 20:
84
- params["page_size"] = page_size
90
+ # Build query parameters - always include all params for clarity
91
+ params = {
92
+ "page": page,
93
+ "page_size": page_size,
94
+ }
85
95
  if search:
86
96
  params["search"] = search
87
97
  if tool_filter:
88
98
  params["tool_filter"] = tool_filter
89
-
99
+
100
+ debug(f"Requesting connections with params: {params}")
90
101
  connections = client.get("/api/v1/user-connections", params=params)
102
+
103
+ debug(f"Received {len(connections) if connections else 0} connections from API")
91
104
 
92
105
  if not connections:
93
- if search or tool_filter:
106
+ if page > 1:
107
+ # User requested a page beyond available data
108
+ error(f"Page {page} is out of range.")
109
+ info(f"Try 'afctl tools list --page 1' to see available connections.")
110
+ if search or tool_filter:
111
+ info(f"Or adjust your search/filter criteria to see more results.")
112
+ elif search or tool_filter:
94
113
  warning(f"No tool connections found matching your criteria. Try adjusting your search or filter.")
95
114
  else:
96
115
  warning("No tool connections found. Add connections in the dashboard UI.")
@@ -114,7 +133,7 @@ def list(
114
133
  "Added": conn.get("created_at", "N/A")[:10] if conn.get("created_at") else "N/A",
115
134
  })
116
135
 
117
- # Show pagination info if not on first page or if filters applied
136
+ # Show pagination and filter info
118
137
  total_info = ""
119
138
  if page != 1 or page_size != 20 or search or tool_filter:
120
139
  total_info = f" (Page {page}, {len(connections)} shown"
@@ -123,6 +142,15 @@ def list(
123
142
  if tool_filter:
124
143
  total_info += f", Tool: '{tool_filter}'"
125
144
  total_info += ")"
145
+
146
+ # Add summary info if filters are active
147
+ if search or tool_filter:
148
+ filter_parts = []
149
+ if search:
150
+ filter_parts.append(f"search='{search}'")
151
+ if tool_filter:
152
+ filter_parts.append(f"tool='{tool_filter}'")
153
+ info(f"🔍 Filtered results: {' AND '.join(filter_parts)}")
126
154
 
127
155
  print_output(
128
156
  display_data,
@@ -132,9 +160,10 @@ def list(
132
160
 
133
161
  # Show helpful tips for pagination
134
162
  if len(connections) == page_size:
135
- info(f"💡 Tip: Use --page {page + 1} to see more results, or --page-size to adjust items per page")
136
- if search or tool_filter:
137
- info(f"💡 Tip: Use --search or --tool filters to find specific connections")
163
+ info(f"💡 Showing {len(connections)} results. Use --page {page + 1} to see more results")
164
+
165
+ if not search and not tool_filter:
166
+ info(f"💡 Tip: Use --search <term> to search, or --tool <type> to filter by tool type")
138
167
 
139
168
  except Exception as e:
140
169
  error(f"Failed to list tool connections: {e}")
@@ -463,6 +492,7 @@ def add(
463
492
  @app.command()
464
493
  def connect(
465
494
  connection_id: str = typer.Argument(..., help="Connection ID to connect"),
495
+ yes: bool = typer.Option(False, "--yes", help="Skip confirmation when reconnecting"),
466
496
  ):
467
497
  """Complete OAuth connection (open browser for authorization)."""
468
498
  try:
@@ -490,9 +520,10 @@ def connect(
490
520
  # Check if connection is already set up (has credentials stored)
491
521
  if connection.get("connected"):
492
522
  warning(f"Connection '{connection_id}' is already connected")
493
- confirm = typer.confirm("Do you want to reconnect (re-authorize)?")
494
- if not confirm:
495
- return
523
+ if not yes:
524
+ confirm = typer.confirm("Do you want to reconnect (re-authorize)?")
525
+ if not confirm:
526
+ return
496
527
 
497
528
  # Determine the API base tool name
498
529
  # - Google tools all use "google" for API credentials, "google_oauth" for oauth3
@@ -63,7 +63,12 @@ class AFClient:
63
63
  def get(self, path: str, params: Optional[Dict] = None) -> Dict[str, Any]:
64
64
  """Make GET request."""
65
65
  url = urljoin(self.config.gateway_url, path)
66
- debug(f"GET {url}")
66
+ if params:
67
+ # Show params in debug output
68
+ param_str = "&".join(f"{k}={v}" for k, v in params.items())
69
+ debug(f"GET {url}?{param_str}")
70
+ else:
71
+ debug(f"GET {url}")
67
72
 
68
73
  response = self.client.get(
69
74
  path,
@@ -13,7 +13,7 @@ from pydantic import BaseModel, Field
13
13
  class CLIConfig(BaseModel):
14
14
  """CLI configuration model."""
15
15
 
16
- # Gateway settings (support environment variable override)
16
+ # Gateway settings (support environment variable override)
17
17
  gateway_url: str = Field(
18
18
  default_factory=lambda: os.getenv("AF_GATEWAY_URL", "https://dashboard.agenticfabriq.com"),
19
19
  description="Gateway URL"
@@ -39,6 +39,7 @@ class CLIConfig(BaseModel):
39
39
  config_file: str = Field(default="", description="Configuration file path")
40
40
  verbose: bool = Field(default=False, description="Verbose output")
41
41
  output_format: str = Field(default="table", description="Output format (table, json, yaml)")
42
+ page_size: int = Field(default=20, description="Default page size for list commands")
42
43
 
43
44
  def __init__(self, **data):
44
45
  super().__init__(**data)
@@ -12,8 +12,6 @@ from rich.table import Table
12
12
  from af_cli.commands.applications import app as applications_app
13
13
  from af_cli.commands.auth import app as auth_app
14
14
  from af_cli.commands.config import app as config_app
15
- from af_cli.commands.mcp_servers import app as mcp_servers_app
16
- from af_cli.commands.secrets import app as secrets_app
17
15
  from af_cli.commands.tools import app as tools_app
18
16
  from af_cli.core.config import get_config
19
17
  from af_cli.core.output import success, error, info
@@ -31,8 +29,6 @@ app.add_typer(auth_app, name="auth", help="Authentication commands")
31
29
  app.add_typer(config_app, name="config", help="Configuration commands")
32
30
  app.add_typer(tools_app, name="tools", help="Tool management commands")
33
31
  app.add_typer(applications_app, name="applications", help="Application management commands")
34
- app.add_typer(mcp_servers_app, name="mcp-servers", help="MCP server management commands")
35
- app.add_typer(secrets_app, name="secrets", help="Secret management commands")
36
32
 
37
33
 
38
34
  @app.command()
@@ -92,19 +88,19 @@ def init(
92
88
  "--tenant-id",
93
89
  help="Tenant ID"
94
90
  ),
95
- force: bool = typer.Option(
91
+ yes: bool = typer.Option(
96
92
  False,
97
- "--force",
98
- help="Force initialization, overwrite existing config"
93
+ "--yes",
94
+ help="Skip confirmation and overwrite existing config"
99
95
  ),
100
96
  ):
101
97
  """Initialize CLI configuration."""
102
98
  config = get_config()
103
99
 
104
100
  # Check if config exists
105
- if os.path.exists(config.config_file) and not force:
101
+ if os.path.exists(config.config_file) and not yes:
106
102
  error(f"Configuration already exists at {config.config_file}")
107
- error("Use --force to overwrite")
103
+ error("Use --yes to overwrite")
108
104
  raise typer.Exit(1)
109
105
 
110
106
  # Create config directory if it doesn't exist
@@ -152,7 +148,7 @@ def main(
152
148
  """
153
149
  Agentic Fabric CLI - Manage your connectivity hub.
154
150
 
155
- The CLI provides commands for managing agents, tools, MCP servers, and secrets
151
+ The CLI provides commands for managing tool connections and applications
156
152
  in your Agentic Fabric deployment.
157
153
  """
158
154
  # Configure global options
@@ -7,7 +7,6 @@ from .base import (
7
7
  BaseConnector,
8
8
  ConnectorContext,
9
9
  HTTPConnectorMixin,
10
- MCPConnector,
11
10
  ToolConnector,
12
11
  )
13
12
  from .registry import ConnectorRegistry
@@ -16,7 +15,6 @@ __all__ = [
16
15
  "BaseConnector",
17
16
  "ToolConnector",
18
17
  "AgentConnector",
19
- "MCPConnector",
20
18
  "ConnectorContext",
21
19
  "HTTPConnectorMixin",
22
20
  "ConnectorRegistry",
@@ -105,7 +105,7 @@ class ToolConnector(BaseConnector):
105
105
 
106
106
 
107
107
  class AgentConnector(BaseConnector):
108
- """Base class for agent connectors that speak A2A or MCP."""
108
+ """Base class for agent connectors that speak A2A."""
109
109
 
110
110
  AGENT_ID: str = ""
111
111
 
@@ -144,47 +144,6 @@ class AgentConnector(BaseConnector):
144
144
  return True
145
145
 
146
146
 
147
- class MCPConnector(BaseConnector):
148
- """Base class for MCP (Model Context Protocol) connectors."""
149
-
150
- MCP_VERSION: str = "1.0"
151
- SERVER_NAME: str = ""
152
-
153
- def __init__(self, ctx: ConnectorContext):
154
- super().__init__(ctx)
155
- if not self.SERVER_NAME:
156
- raise ConnectorError("SERVER_NAME must be set in MCP connector subclass")
157
-
158
- @abc.abstractmethod
159
- async def list_tools(self) -> list[Dict[str, Any]]:
160
- """List available tools from the MCP server."""
161
- pass
162
-
163
- @abc.abstractmethod
164
- async def call_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Any:
165
- """Call a specific tool on the MCP server."""
166
- pass
167
-
168
- async def invoke(self, method: str, **kwargs) -> Any:
169
- """Generic invoke method for MCP connectors."""
170
- if method == "list_tools":
171
- return await self.list_tools()
172
- elif method == "call_tool":
173
- return await self.call_tool(
174
- kwargs.get("tool_name", ""), kwargs.get("arguments", {})
175
- )
176
- else:
177
- raise ConnectorError(f"Unknown method: {method}")
178
-
179
- async def get_server_info(self) -> Dict[str, Any]:
180
- """Get MCP server information."""
181
- return {
182
- "name": self.SERVER_NAME,
183
- "version": self.MCP_VERSION,
184
- "tools": await self.list_tools(),
185
- }
186
-
187
-
188
147
  class HTTPConnectorMixin:
189
148
  """Mixin for connectors that make HTTP requests."""
190
149
 
@@ -1,10 +1,9 @@
1
1
  from .decorators import tool
2
- from .runtime import ToolFabric, MCPServer
2
+ from .runtime import ToolFabric
3
3
 
4
4
  __all__ = [
5
5
  "tool",
6
6
  "ToolFabric",
7
- "MCPServer",
8
7
  ]
9
8
 
10
9
 
@@ -40,25 +40,3 @@ class ToolFabric:
40
40
  r.raise_for_status()
41
41
  return r.json()
42
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
-
@@ -3,7 +3,6 @@ FabriqClient: High-level async helper for Agentic Fabric Gateway.
3
3
 
4
4
  This client wraps common Gateway API flows so agent developers can:
5
5
  - List and invoke tools
6
- - Register and invoke MCP servers (via proxy)
7
6
  - Invoke agents (via proxy)
8
7
  - Manage per-user secrets via the Gateway-backed Vault API
9
8
 
@@ -120,43 +119,6 @@ class FabriqClient:
120
119
  )
121
120
  return r.json()
122
121
 
123
- # -----------------
124
- # MCP Servers
125
- # -----------------
126
- async def register_mcp_server(
127
- self,
128
- *,
129
- name: str,
130
- base_url: str,
131
- description: Optional[str] = None,
132
- auth_type: str = "API_KEY",
133
- source: str = "STATIC",
134
- metadata: Optional[Dict[str, Any]] = None,
135
- ) -> Dict[str, Any]:
136
- body: Dict[str, Any] = {
137
- "name": name,
138
- "base_url": base_url,
139
- "auth_type": auth_type,
140
- "source": source,
141
- }
142
- if description is not None:
143
- body["description"] = description
144
- if metadata is not None:
145
- body["metadata"] = metadata
146
- r = await self._http.post("/mcp/servers", json=body, headers=self._extra_headers)
147
- return r.json()
148
-
149
- async def list_mcp_servers(self, *, page: int = 1, page_size: int = 20, search: Optional[str] = None) -> Dict[str, Any]:
150
- params: Dict[str, Any] = {"page": page, "page_size": page_size}
151
- if search:
152
- params["search"] = search
153
- r = await self._http.get("/mcp/servers", params=params, headers=self._extra_headers)
154
- return r.json()
155
-
156
- async def invoke_mcp(self, *, server_id: str, payload: Dict[str, Any], raw: bool = False) -> Dict[str, Any]:
157
- r = await self._http.post(f"/proxy/mcp/{server_id}/invoke", json={"payload": payload, "raw": raw}, headers=self._extra_headers)
158
- return r.json()
159
-
160
122
  # -----------------
161
123
  # Secrets (Gateway-backed Vault)
162
124
  # -----------------
@@ -5,8 +5,6 @@ Data models for Agentic Fabric SDK.
5
5
  from .types import (
6
6
  ErrorResponse,
7
7
  HealthResponse,
8
- McpServer,
9
- McpServerCreate,
10
8
  MetricsResponse,
11
9
  OAuthToken,
12
10
  PaginatedResponse,
@@ -24,8 +22,6 @@ __all__ = [
24
22
  "Tool",
25
23
  "ToolInvokeRequest",
26
24
  "ToolInvokeResult",
27
- "McpServer",
28
- "McpServerCreate",
29
25
  "Secret",
30
26
  "SecretMetadata",
31
27
  "SecretPutRequest",
@@ -75,28 +75,6 @@ class ToolInvokeResult(BaseModel):
75
75
  headers: Dict[str, str] = Field(default_factory=dict)
76
76
 
77
77
 
78
- class McpServer(BaseModel):
79
- """MCP Server model."""
80
-
81
- id: UUID
82
- base_url: str
83
- auth_type: str = Field(description="One of: API_KEY, MTLS, OIDC")
84
- source: str = Field(description="One of: STATIC, DISCOVERED, SLACK, CONFIG")
85
- tenant_id: str
86
- metadata: Dict[str, Any] = Field(default_factory=dict)
87
- created_at: datetime
88
- updated_at: datetime
89
-
90
-
91
- class McpServerCreate(BaseModel):
92
- """Request model for creating an MCP server."""
93
-
94
- base_url: str
95
- auth_type: str = Field(description="One of: API_KEY, MTLS, OIDC")
96
- source: str = Field(description="One of: STATIC, DISCOVERED, SLACK, CONFIG")
97
- metadata: Dict[str, Any] = Field(default_factory=dict)
98
-
99
-
100
78
  class Secret(BaseModel):
101
79
  """Secret model."""
102
80
 
@@ -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.24"
8
+ version = "0.1.25"
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"
@@ -1,8 +0,0 @@
1
- """
2
- Agentic Fabric CLI Tool
3
-
4
- A command-line interface for managing Agentic Fabric resources including
5
- agents, tools, MCP servers, and administrative operations.
6
- """
7
-
8
- __version__ = "1.0.0"
@@ -1,83 +0,0 @@
1
- """
2
- MCP server management commands for the Agentic Fabric CLI.
3
- """
4
-
5
- import typer
6
-
7
- from af_cli.core.client import get_client
8
- from af_cli.core.output import error, info, print_output, success, warning
9
-
10
- app = typer.Typer(help="MCP server management commands")
11
-
12
-
13
- @app.command()
14
- def list(
15
- format: str = typer.Option("table", "--format", help="Output format"),
16
- ):
17
- """List MCP servers."""
18
- try:
19
- with get_client() as client:
20
- response = client.get("/api/v1/mcp-servers")
21
- servers = response["servers"]
22
-
23
- if not servers:
24
- warning("No MCP servers found")
25
- return
26
-
27
- print_output(
28
- servers,
29
- format_type=format,
30
- title="MCP Servers"
31
- )
32
-
33
- except Exception as e:
34
- error(f"Failed to list MCP servers: {e}")
35
- raise typer.Exit(1)
36
-
37
-
38
- @app.command()
39
- def get(
40
- server_id: str = typer.Argument(..., help="MCP server ID"),
41
- format: str = typer.Option("table", "--format", help="Output format"),
42
- ):
43
- """Get MCP server details."""
44
- try:
45
- with get_client() as client:
46
- server = client.get(f"/api/v1/mcp-servers/{server_id}")
47
-
48
- print_output(
49
- server,
50
- format_type=format,
51
- title=f"MCP Server {server_id}"
52
- )
53
-
54
- except Exception as e:
55
- error(f"Failed to get MCP server: {e}")
56
- raise typer.Exit(1)
57
-
58
-
59
- @app.command()
60
- def create(
61
- name: str = typer.Option(..., "--name", help="MCP server name"),
62
- base_url: str = typer.Option(..., "--base-url", help="Base URL"),
63
- auth_type: str = typer.Option("API_KEY", "--auth-type", help="Authentication type"),
64
- ):
65
- """Create a new MCP server."""
66
- try:
67
- with get_client() as client:
68
- data = {
69
- "name": name,
70
- "base_url": base_url,
71
- "auth_type": auth_type,
72
- "source": "STATIC",
73
- }
74
-
75
- server = client.post("/api/v1/mcp-servers", data)
76
-
77
- success(f"MCP server created: {server['id']}")
78
- info(f"Name: {server['name']}")
79
- info(f"Base URL: {server['base_url']}")
80
-
81
- except Exception as e:
82
- error(f"Failed to create MCP server: {e}")
83
- raise typer.Exit(1)
@@ -1,109 +0,0 @@
1
- """
2
- Secret management commands for the Agentic Fabric CLI.
3
- """
4
-
5
- import typer
6
-
7
- from af_cli.core.client import get_client
8
- from af_cli.core.output import error, info, print_output, success
9
-
10
- app = typer.Typer(help="Secret management commands")
11
-
12
-
13
- @app.command()
14
- def get(
15
- path: str = typer.Argument(..., help="Secret path"),
16
- format: str = typer.Option("table", "--format", help="Output format"),
17
- ):
18
- """Get a secret."""
19
- try:
20
- with get_client() as client:
21
- secret = client.get(f"/api/v1/secrets/{path}")
22
-
23
- # Don't display the actual secret value in table format
24
- if format == "table":
25
- display_data = {
26
- "path": secret["path"],
27
- "description": secret.get("description", ""),
28
- "version": secret["version"],
29
- "created_at": secret["created_at"],
30
- "updated_at": secret["updated_at"],
31
- }
32
- print_output(display_data, format_type=format, title=f"Secret {path}")
33
- info("Use --format=json to see the secret value")
34
- else:
35
- print_output(secret, format_type=format)
36
-
37
- except Exception as e:
38
- error(f"Failed to get secret: {e}")
39
- raise typer.Exit(1)
40
-
41
-
42
- @app.command()
43
- def create(
44
- path: str = typer.Argument(..., help="Secret path"),
45
- value: str = typer.Option(..., "--value", help="Secret value"),
46
- description: str = typer.Option("", "--description", help="Secret description"),
47
- ):
48
- """Create a new secret."""
49
- try:
50
- with get_client() as client:
51
- data = {
52
- "value": value,
53
- "description": description,
54
- }
55
-
56
- secret = client.post(f"/api/v1/secrets/{path}", data)
57
-
58
- success(f"Secret created: {secret['path']}")
59
- info(f"Version: {secret['version']}")
60
-
61
- except Exception as e:
62
- error(f"Failed to create secret: {e}")
63
- raise typer.Exit(1)
64
-
65
-
66
- @app.command()
67
- def update(
68
- path: str = typer.Argument(..., help="Secret path"),
69
- value: str = typer.Option(..., "--value", help="Secret value"),
70
- description: str = typer.Option("", "--description", help="Secret description"),
71
- ):
72
- """Update a secret."""
73
- try:
74
- with get_client() as client:
75
- data = {
76
- "value": value,
77
- "description": description,
78
- }
79
-
80
- secret = client.put(f"/api/v1/secrets/{path}", data)
81
-
82
- success(f"Secret updated: {secret['path']}")
83
- info(f"Version: {secret['version']}")
84
-
85
- except Exception as e:
86
- error(f"Failed to update secret: {e}")
87
- raise typer.Exit(1)
88
-
89
-
90
- @app.command()
91
- def delete(
92
- path: str = typer.Argument(..., help="Secret path"),
93
- force: bool = typer.Option(False, "--force", help="Force deletion"),
94
- ):
95
- """Delete a secret."""
96
- try:
97
- if not force:
98
- if not typer.confirm(f"Are you sure you want to delete secret {path}?"):
99
- info("Deletion cancelled")
100
- return
101
-
102
- with get_client() as client:
103
- client.delete(f"/api/v1/secrets/{path}")
104
-
105
- success(f"Secret deleted: {path}")
106
-
107
- except Exception as e:
108
- error(f"Failed to delete secret: {e}")
109
- raise typer.Exit(1)