agentify-toolkit 0.18.2__tar.gz → 0.19.0__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.
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/PKG-INFO +2 -2
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/README.md +1 -1
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/pyproject.toml +1 -1
- agentify_toolkit-0.18.2/src/agentify/tool.py → agentify_toolkit-0.19.0/src/agentify/_tool.py +1 -1
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/agent.py +151 -50
- agentify_toolkit-0.19.0/src/agentify/cli.py +57 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/commands/__init__.py +5 -1
- agentify_toolkit-0.19.0/src/agentify/commands/_mcp.py +183 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/commands/agent.py +1 -1
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/commands/deploy.py +1 -1
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/commands/gateway.py +1 -1
- agentify_toolkit-0.19.0/src/agentify/commands/mcp.py +168 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/commands/provider.py +1 -1
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/commands/run.py +1 -1
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/commands/runtime.py +4 -4
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/commands/serve.py +1 -1
- agentify_toolkit-0.19.0/src/agentify/commands/tool.py +166 -0
- agentify_toolkit-0.19.0/src/agentify/headers.py +45 -0
- agentify_toolkit-0.19.0/src/agentify/mcp/__init__.py +2 -0
- agentify_toolkit-0.19.0/src/agentify/mcp/builtin_tools.py +47 -0
- agentify_toolkit-0.19.0/src/agentify/mcp/client.py +137 -0
- agentify_toolkit-0.19.0/src/agentify/mcp/registry.py +20 -0
- agentify_toolkit-0.19.0/src/agentify/mcp/server.py +178 -0
- agentify_toolkit-0.19.0/src/agentify/mcp_client.py +23 -0
- agentify_toolkit-0.19.0/src/agentify/mcpserver/__init__.py +1 -0
- agentify_toolkit-0.19.0/src/agentify/mcpserver/server.py +237 -0
- agentify_toolkit-0.19.0/src/agentify/tool.py +215 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify_toolkit.egg-info/PKG-INFO +2 -2
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify_toolkit.egg-info/SOURCES.txt +12 -0
- agentify_toolkit-0.18.2/src/agentify/cli.py +0 -32
- agentify_toolkit-0.18.2/src/agentify/commands/tool.py +0 -72
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/LICENSE +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/NOTICE +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/setup.cfg +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/__init__.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/_cli.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/cli_config.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/cli_ui.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/commands/config.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/gateway/server.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/providers/__init__.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/providers/agentify.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/providers/anthropic.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/providers/backplane.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/providers/bedrock.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/providers/deepseek.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/providers/github.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/providers/google.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/providers/mistral.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/providers/ollama.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/providers/ollama_local.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/providers/openai.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/providers/x.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/runtime/__init__.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/runtime/server.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/runtime_client.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/server/__init__.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/server/server.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/specs.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/ui/agent_list.css +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/ui/agent_list.html +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/ui/basic-chat.css +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/ui/chat.css +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/ui/chat.html +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/ui/fun_agent_list.html +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/ui/htmx.min.js +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/ui/retro-chat.css +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/ui/runtime_chat.html +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify/utils/env_manager.py +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify_toolkit.egg-info/dependency_links.txt +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify_toolkit.egg-info/entry_points.txt +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify_toolkit.egg-info/requires.txt +0 -0
- {agentify_toolkit-0.18.2 → agentify_toolkit-0.19.0}/src/agentify_toolkit.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentify_toolkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.19.0
|
|
4
4
|
Summary: Python Toolkit for Declarative AI Agent Development
|
|
5
5
|
Author-email: Lewis Sheridan <lewis@backplane.dev>
|
|
6
6
|
License: Apache License
|
|
@@ -142,7 +142,7 @@ Dynamic: license-file
|
|
|
142
142
|
|
|
143
143
|

|
|
144
144
|
|
|
145
|
-

|
|
146
146
|
|
|
147
147
|
Agentify is a lightweight, declarative-first toolkit for prototyping AI agents. It lets you define agents as YAML specs and test them rapidly from the CLI or Python, without committing to a framework or model provider.
|
|
148
148
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|

|
|
11
11
|
|
|
12
|
-

|
|
13
13
|
|
|
14
14
|
Agentify is a lightweight, declarative-first toolkit for prototyping AI agents. It lets you define agents as YAML specs and test them rapidly from the CLI or Python, without committing to a framework or model provider.
|
|
15
15
|
|
agentify_toolkit-0.18.2/src/agentify/tool.py → agentify_toolkit-0.19.0/src/agentify/_tool.py
RENAMED
|
@@ -132,7 +132,7 @@ def create_tool(spec: dict) -> Tool:
|
|
|
132
132
|
|
|
133
133
|
# Create the Tool object
|
|
134
134
|
tool = Tool(
|
|
135
|
-
name=spec[
|
|
135
|
+
name=f"local.{spec['name']}",
|
|
136
136
|
type=spec.get("type" or "remote"),
|
|
137
137
|
description=spec.get("description", ""),
|
|
138
138
|
vendor=spec.get("vendor", ""),
|
|
@@ -10,6 +10,10 @@ from .specs import load_tool_spec
|
|
|
10
10
|
from .tool import create_tool
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
|
|
13
|
+
# Import MCP Client
|
|
14
|
+
# from agentify.mcp_client import MCPClient
|
|
15
|
+
from agentify.mcp.client import MCPClientHTTP
|
|
16
|
+
|
|
13
17
|
|
|
14
18
|
@dataclass
|
|
15
19
|
class Agent:
|
|
@@ -23,6 +27,9 @@ class Agent:
|
|
|
23
27
|
tool_names: list = field(default_factory=list)
|
|
24
28
|
tools: dict = field(default_factory=dict)
|
|
25
29
|
|
|
30
|
+
# mcp_client: Optional["MCPClientHTTP"] = None
|
|
31
|
+
mcp_clients: list[MCPClientHTTP] = field(default_factory=list)
|
|
32
|
+
|
|
26
33
|
agent_file: Path | None = None
|
|
27
34
|
_tools_loaded: bool = field(default=False, init=False)
|
|
28
35
|
|
|
@@ -59,7 +66,7 @@ class Agent:
|
|
|
59
66
|
raise FileNotFoundError(f"Tool '{tool_name}' not found at {tool_file}")
|
|
60
67
|
|
|
61
68
|
spec = load_tool_spec(tool_file) # your YAML loader
|
|
62
|
-
tool = create_tool(spec) # your tool factory
|
|
69
|
+
tool = create_tool(spec, tool_file) # your tool factory
|
|
63
70
|
self.tools[tool.name] = tool
|
|
64
71
|
|
|
65
72
|
self._tools_loaded = True
|
|
@@ -101,35 +108,71 @@ class Agent:
|
|
|
101
108
|
|
|
102
109
|
|
|
103
110
|
|
|
104
|
-
def chat(
|
|
111
|
+
def chat(self, debug: bool = False):
|
|
105
112
|
from rich.console import Console
|
|
106
113
|
from rich.panel import Panel
|
|
107
114
|
from rich.prompt import Prompt
|
|
108
115
|
|
|
109
|
-
# Load Tools
|
|
110
|
-
if
|
|
111
|
-
|
|
116
|
+
# Load Tools from local Files in tools/
|
|
117
|
+
if self.tool_names and not self._tools_loaded:
|
|
118
|
+
self.load_tools()
|
|
119
|
+
|
|
120
|
+
# MCP Tool loader
|
|
121
|
+
# if self.mcp_client:
|
|
122
|
+
# self.mcp_client.initialize()
|
|
123
|
+
# mcp_tools = self.mcp_client.list_tools()
|
|
124
|
+
# mcp_tool_names = [t["name"] for t in mcp_tools]
|
|
125
|
+
# else:
|
|
126
|
+
# mcp_tool_names = []
|
|
127
|
+
|
|
128
|
+
# MCP Tool loader v2
|
|
129
|
+
|
|
130
|
+
mcp_tools = []
|
|
131
|
+
mcp_tool_names = []
|
|
132
|
+
|
|
133
|
+
for client in self.mcp_clients:
|
|
134
|
+
client.initialize()
|
|
135
|
+
tools = client.list_tools()
|
|
136
|
+
# mcp_tools += tools
|
|
137
|
+
# mcp_tool_names += [t["name"] for t in tools]
|
|
138
|
+
|
|
139
|
+
for tool in tools:
|
|
140
|
+
namespaced_tool = tool.copy()
|
|
141
|
+
|
|
142
|
+
original_name = tool["name"]
|
|
143
|
+
namespaced_name = f"{client.name}.{original_name}"
|
|
144
|
+
|
|
145
|
+
namespaced_tool["name"] = namespaced_name
|
|
146
|
+
|
|
147
|
+
mcp_tools.append(namespaced_tool)
|
|
148
|
+
mcp_tool_names.append(namespaced_name)
|
|
112
149
|
|
|
113
150
|
console = Console()
|
|
114
151
|
|
|
115
152
|
# Print agent header
|
|
116
153
|
console.print(Panel(
|
|
117
|
-
f"[bold cyan]{
|
|
118
|
-
f"Role: {
|
|
119
|
-
f"Using [yellow]{
|
|
120
|
-
f"Tools:
|
|
154
|
+
f"[bold cyan]{self.name.upper()}[/bold cyan] [dim]{self.version}[/dim]\n"
|
|
155
|
+
f"Role: {self.description}\n"
|
|
156
|
+
f"Using [yellow]{self.model_id}[/yellow] by {self.provider}\n"
|
|
157
|
+
f"Agent Tools: {self.tool_names}\n"
|
|
158
|
+
f"MCP Server Tools: {mcp_tool_names}",
|
|
121
159
|
border_style="cyan"
|
|
122
160
|
))
|
|
123
161
|
|
|
124
162
|
|
|
125
|
-
#
|
|
126
|
-
tool_schemas = [tool.to_schema() for tool in
|
|
163
|
+
# Load Local Tool Schemas
|
|
164
|
+
tool_schemas = [tool.to_schema() for tool in self.tools.values()] if self.tools else None
|
|
127
165
|
tools_block = ""
|
|
128
166
|
if tool_schemas:
|
|
129
|
-
tools_block = "\n\
|
|
167
|
+
tools_block = "\n\nLOCAL TOOLS:\n" + json.dumps(tool_schemas, indent=2)
|
|
168
|
+
|
|
169
|
+
# Load MCP Server Tools
|
|
170
|
+
if self.mcp_clients:
|
|
171
|
+
tools_block += "\n\nMCP TOOLS:\n" + json.dumps(mcp_tools, indent=2)
|
|
172
|
+
|
|
130
173
|
|
|
131
174
|
if debug:
|
|
132
|
-
console.print(
|
|
175
|
+
console.print(tools_block)
|
|
133
176
|
|
|
134
177
|
while True:
|
|
135
178
|
prompt = Prompt.ask("\nEnter your prompt ('/exit' to quit)")
|
|
@@ -138,37 +181,47 @@ class Agent:
|
|
|
138
181
|
break
|
|
139
182
|
|
|
140
183
|
# Add user input to conversation history
|
|
141
|
-
|
|
184
|
+
self.conversation_history.append({"role": "user", "content": prompt})
|
|
142
185
|
|
|
143
186
|
# Build full prompt from last N turns (e.g., last 6)
|
|
144
|
-
full_prompt = f"You must assume the role of {
|
|
145
|
-
for turn in
|
|
187
|
+
full_prompt = f"You must assume the role of {self.role} when responding to these prompts:\n\n"
|
|
188
|
+
for turn in self.conversation_history[-6:]:
|
|
146
189
|
role = turn["role"]
|
|
147
190
|
content = turn["content"]
|
|
148
|
-
full_prompt += f"{role.upper()}: {content}\n"
|
|
191
|
+
full_prompt += f"Conversation History\n {role.upper()}: {content}\n"
|
|
149
192
|
|
|
150
193
|
# Inject tool rules if tools exist
|
|
151
194
|
if tool_schemas:
|
|
152
195
|
full_prompt += """
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
196
|
+
Respond to user requests naturally, but if a tool must be invoked, respond with ONLY raw JSON that is parseable, with no code fences, no extra labels, and no commentary.
|
|
197
|
+
|
|
198
|
+
Rules:
|
|
199
|
+
|
|
200
|
+
1. If the request is "list tools", respond with a numbered list of all tools in this pattern:
|
|
201
|
+
1. <tool name>: <description>: <arguments>: <type> (local or remote)
|
|
202
|
+
|
|
203
|
+
2. When invoking a tool, respond ONLY with the JSON object in this exact format:
|
|
204
|
+
{
|
|
205
|
+
"tool": "<tool name>",
|
|
206
|
+
"action": "<action name>",
|
|
207
|
+
"args": {...}
|
|
208
|
+
}
|
|
209
|
+
Do NOT wrap this in markdown, code blocks, backticks, or any extra text. The JSON must be parseable directly.
|
|
210
|
+
|
|
211
|
+
3. Only use a tool if necessary. If no tool is needed, respond in plain language.
|
|
212
|
+
|
|
213
|
+
4. Use the tool arguments exactly as defined in the schema.
|
|
214
|
+
|
|
215
|
+
When a user requests a tool action, produce only the JSON object following the above format.
|
|
166
216
|
"""
|
|
167
217
|
full_prompt += tools_block
|
|
218
|
+
|
|
219
|
+
if debug:
|
|
220
|
+
console.print(full_prompt)
|
|
168
221
|
|
|
169
222
|
# Send prompt to model
|
|
170
|
-
with console.status(f"[
|
|
171
|
-
response =
|
|
223
|
+
with console.status(f"[green]{self.name.title()} is thinking...[/green]", spinner="dots"):
|
|
224
|
+
response = self.run(full_prompt)
|
|
172
225
|
|
|
173
226
|
# Try parsing JSON (tool invocation)
|
|
174
227
|
try:
|
|
@@ -180,43 +233,64 @@ class Agent:
|
|
|
180
233
|
cleaned = cleaned[4:]
|
|
181
234
|
cleaned = cleaned.strip()
|
|
182
235
|
|
|
236
|
+
# LLM JSON RESPONSE
|
|
183
237
|
data = json.loads(cleaned)
|
|
184
238
|
tool_name = data.get("tool")
|
|
185
239
|
action_name = data.get("action")
|
|
186
240
|
args = data.get("args", {})
|
|
187
|
-
tool =
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
241
|
+
tool = self.tools.get(tool_name)
|
|
242
|
+
|
|
243
|
+
if tool:
|
|
244
|
+
# TOOL HANDLING
|
|
245
|
+
if tool.type == "internal":
|
|
246
|
+
# Local Function Tool
|
|
247
|
+
console.print(f"USING LOCAL TOOL: '{tool_name}' with args: {args}", style="white on green")
|
|
248
|
+
tool_result = tool.invoke(args=args)
|
|
249
|
+
else:
|
|
250
|
+
# Local API Tool
|
|
251
|
+
console.print(f"USING LOCAL TOOL: '{tool_name}' action '{action_name}' with args: {args}", style="bold black on yellow")
|
|
252
|
+
tool_result = tool.invoke(action_name, args)
|
|
196
253
|
else:
|
|
197
|
-
#
|
|
198
|
-
|
|
199
|
-
|
|
254
|
+
# Check MCP
|
|
255
|
+
if tool_name in mcp_tool_names:
|
|
256
|
+
# MCP SERVER TOOL
|
|
257
|
+
console.print(f"USING MCP SERVER TOOL: '{tool_name}' with args: {args}", style="bold black on yellow")
|
|
258
|
+
# tool_result = self.mcp_client.call_tool(tool_name, args)
|
|
259
|
+
server_name, actual_tool = tool_name.split(".", 1)
|
|
260
|
+
|
|
261
|
+
client = next(
|
|
262
|
+
(c for c in self.mcp_clients if c.name == server_name),
|
|
263
|
+
None
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
if not client:
|
|
267
|
+
raise Exception(f"No MCP client found for server '{server_name}'")
|
|
268
|
+
|
|
269
|
+
tool_result = client.call_tool(actual_tool, args)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
# if not tool:
|
|
273
|
+
# raise ValueError(f"Tool '{tool_name}' not found on agent")
|
|
200
274
|
|
|
201
275
|
# Minify JSON to avoid confusing model in next prompt
|
|
202
276
|
tool_result_str = json.dumps(tool_result, separators=(',', ':'))
|
|
203
277
|
|
|
204
278
|
# Add tool output to conversation history
|
|
205
|
-
|
|
279
|
+
self.conversation_history.append({"role": "tool", "content": tool_result_str})
|
|
206
280
|
|
|
207
281
|
# Ask model to display tool output naturally
|
|
208
282
|
analysis_prompt = "Display the following tool data in natural language:\n" + tool_result_str
|
|
209
|
-
with console.status(f"
|
|
210
|
-
response =
|
|
283
|
+
with console.status(f"{self.name.title()} is analysing tool response...", spinner="dots"):
|
|
284
|
+
response = self.run(analysis_prompt)
|
|
211
285
|
|
|
212
286
|
# Store agent response in history
|
|
213
|
-
|
|
287
|
+
self.conversation_history.append({"role": "agent", "content": response})
|
|
214
288
|
console.print(Panel.fit(response, title="Agent Response", border_style="green"))
|
|
215
289
|
|
|
216
290
|
|
|
217
291
|
except (json.JSONDecodeError, ValueError):
|
|
218
292
|
# Treat as normal chat response
|
|
219
|
-
|
|
293
|
+
self.conversation_history.append({"role": "agent", "content": response})
|
|
220
294
|
console.print(Panel.fit(response, title="Agent Response", border_style="green"))
|
|
221
295
|
|
|
222
296
|
def create_agents(specs: list) -> dict[str, Agent]:
|
|
@@ -244,8 +318,35 @@ def create_agent(spec: dict, provider: str = None, model: str = None, agent_file
|
|
|
244
318
|
if api_key_env:
|
|
245
319
|
api_key = os.getenv(api_key_env)
|
|
246
320
|
|
|
321
|
+
# Local Tools via tool.yaml or tool.yaml/tool.py within tools/
|
|
247
322
|
tool_names = spec.get("tools")
|
|
248
323
|
|
|
249
|
-
|
|
324
|
+
# Load MCP Server clients
|
|
325
|
+
mcp_clients = []
|
|
326
|
+
|
|
327
|
+
mcp_spec = spec.get("mcp", {})
|
|
328
|
+
servers = mcp_spec.get("servers",[])
|
|
329
|
+
|
|
330
|
+
for server in servers:
|
|
331
|
+
server_name = server.get("name")
|
|
332
|
+
endpoint = server.get("endpoint")
|
|
333
|
+
|
|
334
|
+
if endpoint:
|
|
335
|
+
client = MCPClientHTTP(
|
|
336
|
+
name=server_name,
|
|
337
|
+
endpoint=endpoint
|
|
338
|
+
)
|
|
339
|
+
mcp_clients.append(client)
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
# MCP Tools via MCP Server http://localhost:3333
|
|
343
|
+
# mcp_client = None
|
|
344
|
+
# mcp_spec = spec.get("mcp")
|
|
345
|
+
# if mcp_spec:
|
|
346
|
+
# endpoint = mcp_spec.get("endpoint")
|
|
347
|
+
# if endpoint:
|
|
348
|
+
# mcp_client = MCPClientHTTP(endpoint)
|
|
349
|
+
|
|
350
|
+
agent = Agent(name=name, provider=provider, model_id=model_id, role=role, description=description, version=version, tool_names=tool_names, agent_file=agent_file, mcp_clients=mcp_clients)
|
|
250
351
|
|
|
251
352
|
return agent
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from agentify import __version__
|
|
3
|
+
from .commands import (
|
|
4
|
+
run_command,
|
|
5
|
+
serve_command,
|
|
6
|
+
deploy_command,
|
|
7
|
+
gateway_command,
|
|
8
|
+
runtime_group,
|
|
9
|
+
tool_group,
|
|
10
|
+
agent_group,
|
|
11
|
+
provider_group,
|
|
12
|
+
mcp_group,
|
|
13
|
+
xmcp_group
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from rich.console import Console
|
|
17
|
+
from rich.panel import Panel
|
|
18
|
+
|
|
19
|
+
console = Console()
|
|
20
|
+
|
|
21
|
+
agentify_icon = """
|
|
22
|
+
[white] ██ [/white]
|
|
23
|
+
[white] █████████████████[/white] Command: [yellow]agentify provider add <provider_name>[/yellow] [green]# e.g. openai, xai, anthropic[/green]
|
|
24
|
+
[white]░██ ░░███ ░░██[/white] Command: [yellow]agentify agent new[/yellow] [green]# <-- Creates a new Agent[/green]
|
|
25
|
+
[white]░██ ███ ██[/white] [green]# Run Agent[/green]
|
|
26
|
+
[white]░█████████████████[/white] Command: [yellow]agentify run agent.yaml[/yellow]
|
|
27
|
+
[white]░███████ ███████[/white] [green]# Start MCP Server[/green]
|
|
28
|
+
[white]░█████████████████[/white] Command: [yellow]agentify mcp start[/yellow]
|
|
29
|
+
[white]░░░██░░██░░██░░██ [/white] [green]# Start Agent Runtime → Deploy Agent[/green]
|
|
30
|
+
[white] ░██ ░██ ░██ ░██ [/white] Command: [yellow]agentify runtime start[/yellow] [green]→[/green] [yellow]agentify deploy agent.yaml[/yellow]
|
|
31
|
+
[white] ░░ ░░ ░░ ░░ [/white]
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
console.print(Panel(agentify_icon, title=f"AGENTIFY TOOLKIT CLI v{__version__}", subtitle="Build, Run and deploy AI Agents declaratively", border_style="white"))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@click.group()
|
|
39
|
+
@click.version_option(version=__version__, prog_name="Agentify")
|
|
40
|
+
def main():
|
|
41
|
+
"""Agentify Toolkit CLI"""
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
# Attach lazy-loaded commands
|
|
45
|
+
main.add_command(run_command)
|
|
46
|
+
main.add_command(serve_command)
|
|
47
|
+
main.add_command(deploy_command)
|
|
48
|
+
main.add_command(gateway_command)
|
|
49
|
+
main.add_command(runtime_group)
|
|
50
|
+
main.add_command(tool_group)
|
|
51
|
+
main.add_command(agent_group)
|
|
52
|
+
main.add_command(provider_group)
|
|
53
|
+
main.add_command(mcp_group)
|
|
54
|
+
main.add_command(xmcp_group)
|
|
55
|
+
|
|
56
|
+
# if __name__ == "__main__":
|
|
57
|
+
# main()
|
|
@@ -6,6 +6,8 @@ from .agent import agent_group
|
|
|
6
6
|
from .provider import provider_group
|
|
7
7
|
from .deploy import deploy_command
|
|
8
8
|
from .gateway import gateway_command
|
|
9
|
+
from ._mcp import xmcp_group
|
|
10
|
+
from .mcp import mcp_group
|
|
9
11
|
|
|
10
12
|
__all__ = [
|
|
11
13
|
"run_command",
|
|
@@ -15,5 +17,7 @@ __all__ = [
|
|
|
15
17
|
"deploy_command"
|
|
16
18
|
"agent_group",
|
|
17
19
|
"provider_group",
|
|
18
|
-
"gateway_command"
|
|
20
|
+
"gateway_command",
|
|
21
|
+
"mcp_group",
|
|
22
|
+
"xmcp_group"
|
|
19
23
|
]
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# agentify/commands/mcp.py
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import requests
|
|
5
|
+
from agentify.mcpserver.server import start_xmcp_server
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
import json
|
|
9
|
+
from rich.panel import Panel
|
|
10
|
+
from rich.syntax import Syntax
|
|
11
|
+
|
|
12
|
+
console = Console()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.group(hidden=True)
|
|
16
|
+
def xmcp_group():
|
|
17
|
+
"""Manage Agentify Tool server"""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@xmcp_group.command("start")
|
|
22
|
+
@click.option("--host", default="127.0.0.1", help="Host to bind MCP server")
|
|
23
|
+
@click.option("--port", default=3333, help="Port to bind MCP server")
|
|
24
|
+
def start(host: str, port: int):
|
|
25
|
+
"""Start MCP Server"""
|
|
26
|
+
click.echo(f"Starting MCP server on {host}:{port}")
|
|
27
|
+
start_mcp_server(host=host, port=port)
|
|
28
|
+
|
|
29
|
+
@xmcp_group.command("list")
|
|
30
|
+
@click.option("--endpoint", default="http://127.0.0.1:3333", help="MCP server endpoint")
|
|
31
|
+
def list_tools(endpoint: str):
|
|
32
|
+
"""List tools exposed by the MCP server"""
|
|
33
|
+
|
|
34
|
+
url = f"{endpoint}/tools"
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
response = requests.get(url, timeout=5)
|
|
38
|
+
response.raise_for_status()
|
|
39
|
+
except requests.RequestException as e:
|
|
40
|
+
console.print(f"[red]Error connecting to MCP server:[/red] {e}")
|
|
41
|
+
raise SystemExit(1)
|
|
42
|
+
|
|
43
|
+
data = response.json()
|
|
44
|
+
tools = data.get("tools", [])
|
|
45
|
+
|
|
46
|
+
if not tools:
|
|
47
|
+
console.print("[yellow]No tools found.[/yellow]")
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
table = Table(
|
|
51
|
+
title="MCP Tools",
|
|
52
|
+
show_header=True,
|
|
53
|
+
header_style="bold",
|
|
54
|
+
box=None, # 👈 no borders
|
|
55
|
+
pad_edge=False
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
table.add_column("Tool Name", style="cyan", no_wrap=True)
|
|
59
|
+
table.add_column("Description", style="white")
|
|
60
|
+
|
|
61
|
+
for tool in tools:
|
|
62
|
+
table.add_row(
|
|
63
|
+
tool.get("name", ""),
|
|
64
|
+
tool.get("description", "") or ""
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
console.print(table)
|
|
68
|
+
|
|
69
|
+
@xmcp_group.command("invoke")
|
|
70
|
+
@click.argument("tool_name")
|
|
71
|
+
@click.option(
|
|
72
|
+
"--args",
|
|
73
|
+
default="{}",
|
|
74
|
+
help="JSON string of arguments to pass to the tool",
|
|
75
|
+
)
|
|
76
|
+
@click.option(
|
|
77
|
+
"--endpoint",
|
|
78
|
+
default="http://127.0.0.1:3333",
|
|
79
|
+
help="MCP server endpoint",
|
|
80
|
+
)
|
|
81
|
+
def invoke_tool(tool_name: str, args: str, endpoint: str):
|
|
82
|
+
"""Invoke a published tool"""
|
|
83
|
+
try:
|
|
84
|
+
arguments = json.loads(args)
|
|
85
|
+
except json.JSONDecodeError as e:
|
|
86
|
+
console.print(f"[red]Invalid JSON for --args:[/red] {e}")
|
|
87
|
+
raise SystemExit(1)
|
|
88
|
+
|
|
89
|
+
url = f"{endpoint}/tools/{tool_name}/invoke"
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
response = requests.post(
|
|
93
|
+
url,
|
|
94
|
+
json={"arguments": arguments},
|
|
95
|
+
timeout=10,
|
|
96
|
+
)
|
|
97
|
+
response.raise_for_status()
|
|
98
|
+
except requests.HTTPError:
|
|
99
|
+
console.print(
|
|
100
|
+
f"[red]Tool invocation failed:[/red] {response.text}"
|
|
101
|
+
)
|
|
102
|
+
raise SystemExit(1)
|
|
103
|
+
except requests.RequestException as e:
|
|
104
|
+
console.print(f"[red]Error connecting to MCP server:[/red] {e}")
|
|
105
|
+
raise SystemExit(1)
|
|
106
|
+
|
|
107
|
+
data = response.json()
|
|
108
|
+
result = data.get("result")
|
|
109
|
+
|
|
110
|
+
# Pretty output
|
|
111
|
+
if isinstance(result, (dict, list)):
|
|
112
|
+
pretty = json.dumps(result, indent=2)
|
|
113
|
+
syntax = Syntax(pretty, "json", theme="ansi_dark", line_numbers=False)
|
|
114
|
+
console.print(Panel(syntax, title="Result"))
|
|
115
|
+
else:
|
|
116
|
+
console.print(Panel(str(result), title="Result"))
|
|
117
|
+
|
|
118
|
+
@xmcp_group.command()
|
|
119
|
+
@click.argument("tool_name")
|
|
120
|
+
@click.option(
|
|
121
|
+
"--server", default="http://localhost:3333",
|
|
122
|
+
required=False,
|
|
123
|
+
help="MCP server URL (e.g. http://localhost:3333)",
|
|
124
|
+
)
|
|
125
|
+
def remove(tool_name: str, server: str):
|
|
126
|
+
"""
|
|
127
|
+
Remove a tool from the MCP server.
|
|
128
|
+
"""
|
|
129
|
+
url = f"{server.rstrip('/')}/tools/{tool_name}"
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
response = requests.delete(url, timeout=10)
|
|
133
|
+
except requests.RequestException as e:
|
|
134
|
+
raise click.ClickException(f"Failed to connect to MCP server: {e}")
|
|
135
|
+
|
|
136
|
+
if response.status_code != 200:
|
|
137
|
+
try:
|
|
138
|
+
detail = response.json().get("detail")
|
|
139
|
+
except Exception:
|
|
140
|
+
detail = response.text
|
|
141
|
+
raise click.ClickException(
|
|
142
|
+
f"Failed to remove tool ({response.status_code}): {detail}"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
click.echo(f"✓ Tool removed: {tool_name}")
|
|
146
|
+
|
|
147
|
+
@xmcp_group.command("show")
|
|
148
|
+
@click.argument("tool_name")
|
|
149
|
+
@click.option("--server", default="http://localhost:3333", help="MCP server URL")
|
|
150
|
+
def show_tool(tool_name, server):
|
|
151
|
+
"""Show details for a single MCP tool."""
|
|
152
|
+
import requests
|
|
153
|
+
import sys
|
|
154
|
+
from rich.console import Console
|
|
155
|
+
from rich.table import Table
|
|
156
|
+
from rich.json import JSON
|
|
157
|
+
|
|
158
|
+
console = Console()
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
resp = requests.get(f"{server}/tools/{tool_name}")
|
|
162
|
+
resp.raise_for_status()
|
|
163
|
+
except requests.HTTPError as e:
|
|
164
|
+
console.print(f"[red]Error:[/red] {e.response.text}")
|
|
165
|
+
sys.exit(1)
|
|
166
|
+
except Exception as e:
|
|
167
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
168
|
+
sys.exit(1)
|
|
169
|
+
|
|
170
|
+
tool = resp.json()
|
|
171
|
+
|
|
172
|
+
table = Table(title=f"MCP Tool: {tool['name']}", show_header=False)
|
|
173
|
+
table.add_column("Field", style="bold")
|
|
174
|
+
table.add_column("Value")
|
|
175
|
+
|
|
176
|
+
table.add_row("Name", tool["name"])
|
|
177
|
+
table.add_row("Description", tool.get("description", ""))
|
|
178
|
+
|
|
179
|
+
console.print(table)
|
|
180
|
+
|
|
181
|
+
if "input_schema" in tool:
|
|
182
|
+
console.print("\n[bold]Input Schema[/bold]")
|
|
183
|
+
console.print(JSON.from_data(tool["input_schema"]))
|