agentx-kit 0.3.0__tar.gz → 0.4.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.
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/PKG-INFO +33 -1
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/README.md +30 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/pyproject.toml +3 -1
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/__init__.py +1 -1
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/cli.py +30 -0
- agentx_kit-0.4.0/src/agentx/connector/__init__.py +18 -0
- agentx_kit-0.4.0/src/agentx/connector/build.py +111 -0
- agentx_kit-0.4.0/src/agentx/connector/recommend.py +116 -0
- agentx_kit-0.4.0/src/agentx/connector/server.py +108 -0
- agentx_kit-0.4.0/tests/test_connector.py +70 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/.github/workflows/publish.yml +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/.gitignore +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/DESIGN.md +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/LICENSE +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/RESEARCH.md +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/config.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/dashboard/__init__.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/dashboard/app.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/frameworks/__init__.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/frameworks/crewai_agent.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/frameworks/langchain_agent.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/guardrails.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/insights/__init__.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/insights/analyze.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/insights/log.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/insights/optimize.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/insights/tokens.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/memory/__init__.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/memory/store.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/observability.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/prompts/__init__.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/prompts/templates.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/providers/__init__.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/providers/base.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/providers/factory.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/providers/registry.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/rag/__init__.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/rag/pipeline.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/reliability.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/__init__.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/generator.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/prompts_store.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/spec.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/Dockerfile.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/README.md.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/ci.yml.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/docker-compose.yml.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/dockerignore.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/env.example.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/evals/dataset.json.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/evals/run_evals.py.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/gitignore.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/mcp_servers.json.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/pkg/__init__.py.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/pkg/agents.py.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/pkg/config.py.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/pkg/guardrails.py.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/pkg/main.py.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/pkg/memory.py.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/pkg/observability.py.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/pkg/prompts.py.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/pkg/rag.py.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/pkg/server.py.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/pkg/tools.py.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/pyproject.toml.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/templates/skills_seed.json.j2 +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/scaffold/wizard.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/skills/__init__.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/skills/registry.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/structured.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/tools/__init__.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/tools/builtin.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/src/agentx/tools/mcp.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/tests/test_enterprise.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/tests/test_insights.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/tests/test_prompts.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/tests/test_providers.py +0 -0
- {agentx_kit-0.3.0 → agentx_kit-0.4.0}/tests/test_scaffold.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentx-kit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: An open-source, provider-agnostic agentic framework + interactive project scaffolder for LangChain and CrewAI. Pick your LLM provider, agents, RAG, memory, MCP tools and skills — generate a ready-to-run uv project.
|
|
5
5
|
Project-URL: Homepage, https://github.com/muhammadyahiya/agentx-kit
|
|
6
6
|
Project-URL: Repository, https://github.com/muhammadyahiya/agentx-kit
|
|
@@ -59,6 +59,8 @@ Provides-Extra: azure
|
|
|
59
59
|
Requires-Dist: langchain-openai>=0.2.0; extra == 'azure'
|
|
60
60
|
Provides-Extra: bedrock
|
|
61
61
|
Requires-Dist: langchain-aws>=0.2.0; extra == 'bedrock'
|
|
62
|
+
Provides-Extra: connector
|
|
63
|
+
Requires-Dist: mcp>=1.2.0; extra == 'connector'
|
|
62
64
|
Provides-Extra: crewai
|
|
63
65
|
Requires-Dist: crewai>=0.70.0; extra == 'crewai'
|
|
64
66
|
Provides-Extra: dashboard
|
|
@@ -260,6 +262,35 @@ It gives you, live as you edit:
|
|
|
260
262
|
Run it inside a generated AgentX project and it reads/writes that project's
|
|
261
263
|
`prompts.json`; run it anywhere else for a free-form prompt scratchpad.
|
|
262
264
|
|
|
265
|
+
## 🔌 Use as a connector (Claude / Copilot / Codex)
|
|
266
|
+
AgentX-Kit ships an **MCP server**, so any MCP-capable assistant can scaffold a
|
|
267
|
+
complete project from **a single prompt with your problem statement**.
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
pip install "agentx-kit[connector]"
|
|
271
|
+
agentx mcp --print-config # prints the client config below
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Add it to your client (then restart it):
|
|
275
|
+
```jsonc
|
|
276
|
+
// Claude Desktop / Codex / Copilot — under "mcpServers"
|
|
277
|
+
{ "mcpServers": { "agentx-kit": { "command": "agentx", "args": ["mcp"] } } }
|
|
278
|
+
```
|
|
279
|
+
```bash
|
|
280
|
+
# Claude Code one-liner
|
|
281
|
+
claude mcp add agentx-kit -- agentx mcp
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Now just ask, in plain language:
|
|
285
|
+
> *“Build a customer-support agent that answers from our product docs and serves a REST API.”*
|
|
286
|
+
|
|
287
|
+
The assistant calls AgentX-Kit's tools and you get a complete, runnable project:
|
|
288
|
+
- **`recommend_project(problem_statement)`** — suggests framework, provider, agent count, and features.
|
|
289
|
+
- **`create_agent_project(problem_statement, …)`** — generates the project (infers RAG/serve/memory/etc. from the statement, or take explicit overrides / `enterprise=true`) and returns the file tree + key file contents + run steps.
|
|
290
|
+
- **`list_providers`**, **`analyze_prompt`**, **`optimize_prompt`** — provider list + prompt insights.
|
|
291
|
+
|
|
292
|
+
So from one sentence the assistant produces a pre-wired project (prompts already seeded from your use case), ready to `uv sync && uv run`.
|
|
293
|
+
|
|
263
294
|
## 🏢 Enterprise pack
|
|
264
295
|
Generate a production-shaped project with one flag — informed by a survey of
|
|
265
296
|
CrewAI/LangGraph/create-llama/AgentStack/agno/pydantic-ai (see [RESEARCH.md](RESEARCH.md)):
|
|
@@ -309,6 +340,7 @@ llm = build_resilient_chat("openai", "gpt-4o-mini", fallbacks=[("anthropic", "cl
|
|
|
309
340
|
| `observability` | `opentelemetry-*`, `openinference-*` | tracing |
|
|
310
341
|
| `server` | `fastapi`, `uvicorn` | serving |
|
|
311
342
|
| `dashboard` | `streamlit`, `tiktoken`, `pandas` | prompt observability dashboard |
|
|
343
|
+
| `connector` | `mcp` | MCP server for Claude/Copilot/Codex |
|
|
312
344
|
| `all` | everything above | kitchen sink |
|
|
313
345
|
|
|
314
346
|
See [DESIGN.md](DESIGN.md) for the architecture and [RESEARCH.md](RESEARCH.md) for the competitive analysis behind these features.
|
|
@@ -158,6 +158,35 @@ It gives you, live as you edit:
|
|
|
158
158
|
Run it inside a generated AgentX project and it reads/writes that project's
|
|
159
159
|
`prompts.json`; run it anywhere else for a free-form prompt scratchpad.
|
|
160
160
|
|
|
161
|
+
## 🔌 Use as a connector (Claude / Copilot / Codex)
|
|
162
|
+
AgentX-Kit ships an **MCP server**, so any MCP-capable assistant can scaffold a
|
|
163
|
+
complete project from **a single prompt with your problem statement**.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
pip install "agentx-kit[connector]"
|
|
167
|
+
agentx mcp --print-config # prints the client config below
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Add it to your client (then restart it):
|
|
171
|
+
```jsonc
|
|
172
|
+
// Claude Desktop / Codex / Copilot — under "mcpServers"
|
|
173
|
+
{ "mcpServers": { "agentx-kit": { "command": "agentx", "args": ["mcp"] } } }
|
|
174
|
+
```
|
|
175
|
+
```bash
|
|
176
|
+
# Claude Code one-liner
|
|
177
|
+
claude mcp add agentx-kit -- agentx mcp
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Now just ask, in plain language:
|
|
181
|
+
> *“Build a customer-support agent that answers from our product docs and serves a REST API.”*
|
|
182
|
+
|
|
183
|
+
The assistant calls AgentX-Kit's tools and you get a complete, runnable project:
|
|
184
|
+
- **`recommend_project(problem_statement)`** — suggests framework, provider, agent count, and features.
|
|
185
|
+
- **`create_agent_project(problem_statement, …)`** — generates the project (infers RAG/serve/memory/etc. from the statement, or take explicit overrides / `enterprise=true`) and returns the file tree + key file contents + run steps.
|
|
186
|
+
- **`list_providers`**, **`analyze_prompt`**, **`optimize_prompt`** — provider list + prompt insights.
|
|
187
|
+
|
|
188
|
+
So from one sentence the assistant produces a pre-wired project (prompts already seeded from your use case), ready to `uv sync && uv run`.
|
|
189
|
+
|
|
161
190
|
## 🏢 Enterprise pack
|
|
162
191
|
Generate a production-shaped project with one flag — informed by a survey of
|
|
163
192
|
CrewAI/LangGraph/create-llama/AgentStack/agno/pydantic-ai (see [RESEARCH.md](RESEARCH.md)):
|
|
@@ -207,6 +236,7 @@ llm = build_resilient_chat("openai", "gpt-4o-mini", fallbacks=[("anthropic", "cl
|
|
|
207
236
|
| `observability` | `opentelemetry-*`, `openinference-*` | tracing |
|
|
208
237
|
| `server` | `fastapi`, `uvicorn` | serving |
|
|
209
238
|
| `dashboard` | `streamlit`, `tiktoken`, `pandas` | prompt observability dashboard |
|
|
239
|
+
| `connector` | `mcp` | MCP server for Claude/Copilot/Codex |
|
|
210
240
|
| `all` | everything above | kitchen sink |
|
|
211
241
|
|
|
212
242
|
See [DESIGN.md](DESIGN.md) for the architecture and [RESEARCH.md](RESEARCH.md) for the competitive analysis behind these features.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
# PyPI distribution name (import name + CLI stay `agentx`; `agentx` was taken).
|
|
3
3
|
name = "agentx-kit"
|
|
4
|
-
version = "0.
|
|
4
|
+
version = "0.4.0"
|
|
5
5
|
description = "An open-source, provider-agnostic agentic framework + interactive project scaffolder for LangChain and CrewAI. Pick your LLM provider, agents, RAG, memory, MCP tools and skills — generate a ready-to-run uv project."
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
requires-python = ">=3.10,<3.14"
|
|
@@ -60,6 +60,7 @@ observability = [
|
|
|
60
60
|
]
|
|
61
61
|
server = ["fastapi>=0.110.0", "uvicorn[standard]>=0.29.0", "sse-starlette>=2.0.0"]
|
|
62
62
|
dashboard = ["streamlit>=1.40.0", "tiktoken>=0.7.0", "pandas>=2.0.0"]
|
|
63
|
+
connector = ["mcp>=1.2.0"]
|
|
63
64
|
|
|
64
65
|
# ---- Bundles ----
|
|
65
66
|
all = [
|
|
@@ -73,6 +74,7 @@ all = [
|
|
|
73
74
|
"openinference-instrumentation-langchain>=0.1.0",
|
|
74
75
|
"fastapi>=0.110.0", "uvicorn[standard]>=0.29.0", "sse-starlette>=2.0.0",
|
|
75
76
|
"streamlit>=1.40.0", "tiktoken>=0.7.0", "pandas>=2.0.0",
|
|
77
|
+
"mcp>=1.2.0",
|
|
76
78
|
]
|
|
77
79
|
dev = ["pytest>=8.0.0", "pytest-cov>=5.0.0"]
|
|
78
80
|
|
|
@@ -68,6 +68,36 @@ def dashboard(
|
|
|
68
68
|
raise typer.Exit(1) from exc
|
|
69
69
|
|
|
70
70
|
|
|
71
|
+
@app.command()
|
|
72
|
+
def mcp(
|
|
73
|
+
print_config: bool = typer.Option(False, "--print-config", help="Print MCP client config for Claude/Codex/Copilot and exit."),
|
|
74
|
+
) -> None:
|
|
75
|
+
"""Run AgentX-Kit as an MCP server (connector for Claude / Copilot / Codex).
|
|
76
|
+
|
|
77
|
+
Once connected, a single prompt with a problem statement generates a complete
|
|
78
|
+
project. Add it to a client with the config from `agentx mcp --print-config`.
|
|
79
|
+
"""
|
|
80
|
+
if print_config:
|
|
81
|
+
import json
|
|
82
|
+
|
|
83
|
+
from .connector import client_config
|
|
84
|
+
|
|
85
|
+
cfg = client_config()
|
|
86
|
+
console.print("[bold]MCP client config[/] (Claude Desktop / Claude Code / Codex / Copilot):\n")
|
|
87
|
+
console.print(json.dumps(cfg, indent=2))
|
|
88
|
+
console.print("\n[bold]Claude Code one-liner:[/]")
|
|
89
|
+
console.print(" claude mcp add agentx-kit -- agentx mcp")
|
|
90
|
+
console.print("\n[dim]Add the JSON under \"mcpServers\" in your client's MCP config "
|
|
91
|
+
"(e.g. claude_desktop_config.json), then restart the client.[/]")
|
|
92
|
+
return
|
|
93
|
+
try:
|
|
94
|
+
from .connector import run
|
|
95
|
+
except RuntimeError as exc:
|
|
96
|
+
console.print(f"[red]{exc}[/]")
|
|
97
|
+
raise typer.Exit(1) from exc
|
|
98
|
+
run() # stdio; no console output (the client drives it)
|
|
99
|
+
|
|
100
|
+
|
|
71
101
|
def _result_panel(result, spec: ProjectSpec) -> None:
|
|
72
102
|
lines = [f"[bold green]✓[/] Project '{spec.slug}' created at:", f" {result.target_dir}", ""]
|
|
73
103
|
lines += [f" • {m}" for m in result.messages]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""MCP connector — expose AgentX-Kit to Claude / Copilot / Codex.
|
|
2
|
+
|
|
3
|
+
`recommend_spec` and `build_project_from_statement` are pure (no MCP dep);
|
|
4
|
+
`build_server`/`run` require ``agentx-kit[connector]``.
|
|
5
|
+
"""
|
|
6
|
+
from .build import build_project_from_statement
|
|
7
|
+
from .recommend import recommend_spec
|
|
8
|
+
|
|
9
|
+
__all__ = ["recommend_spec", "build_project_from_statement", "build_server", "run", "client_config"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def __getattr__(name: str):
|
|
13
|
+
# Lazy: only import the MCP SDK when the server is actually requested.
|
|
14
|
+
if name in ("build_server", "run", "client_config"):
|
|
15
|
+
from . import server
|
|
16
|
+
|
|
17
|
+
return getattr(server, name)
|
|
18
|
+
raise AttributeError(name)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Turn a problem statement (+ optional overrides) into a generated project.
|
|
2
|
+
|
|
3
|
+
Pure orchestration over the scaffolder — no MCP dependency — so it's testable
|
|
4
|
+
and reusable. The MCP tools in ``server.py`` are thin wrappers around this.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from ..scaffold import AgentSpec, ProjectSpec, generate_project
|
|
11
|
+
from .recommend import recommend_spec
|
|
12
|
+
|
|
13
|
+
_ALL_FEATURES = ["rag", "memory", "mcp", "skills", "observability", "guardrails", "serve", "docker", "ci", "evals"]
|
|
14
|
+
_KEY_FILES_MAX = 6000
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _apply_features(spec: ProjectSpec, features: list[str]) -> None:
|
|
18
|
+
fl = set(features or [])
|
|
19
|
+
spec.use_rag = "rag" in fl
|
|
20
|
+
spec.memory = "both" if "memory" in fl else "none"
|
|
21
|
+
spec.use_mcp = "mcp" in fl
|
|
22
|
+
spec.use_skills = "skills" in fl
|
|
23
|
+
spec.observability = "observability" in fl
|
|
24
|
+
spec.guardrails = "guardrails" in fl
|
|
25
|
+
spec.serve = "serve" in fl
|
|
26
|
+
spec.docker = "docker" in fl
|
|
27
|
+
spec.ci = "ci" in fl
|
|
28
|
+
spec.evals = "evals" in fl
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def build_project_from_statement(
|
|
32
|
+
problem_statement: str,
|
|
33
|
+
name: str = "",
|
|
34
|
+
framework: str = "",
|
|
35
|
+
provider: str = "",
|
|
36
|
+
model: str = "",
|
|
37
|
+
agents: int = 0,
|
|
38
|
+
features: list[str] | None = None,
|
|
39
|
+
enterprise: bool = False,
|
|
40
|
+
output_dir: str = "",
|
|
41
|
+
create_venv: bool = False,
|
|
42
|
+
overwrite: bool = True,
|
|
43
|
+
) -> dict:
|
|
44
|
+
"""Generate a complete project for a problem statement; return a summary."""
|
|
45
|
+
rec = recommend_spec(problem_statement)
|
|
46
|
+
name = name or rec["name"]
|
|
47
|
+
framework = framework or rec["framework"]
|
|
48
|
+
provider = provider or rec["provider"]
|
|
49
|
+
model = model or rec["model"]
|
|
50
|
+
n_agents = agents or rec["agents"]
|
|
51
|
+
feats = list(features) if features is not None else list(rec["features"])
|
|
52
|
+
|
|
53
|
+
# First agent carries the role/goal/prompt derived from the problem statement.
|
|
54
|
+
agent_specs = [AgentSpec(name="assistant" if n_agents == 1 else "agent_1",
|
|
55
|
+
role=rec["role"], goal=rec["goal"], system_prompt=rec["system_prompt"])]
|
|
56
|
+
for i in range(1, n_agents):
|
|
57
|
+
agent_specs.append(AgentSpec(name=f"agent_{i + 1}", role=rec["role"]))
|
|
58
|
+
|
|
59
|
+
spec = ProjectSpec(
|
|
60
|
+
name=name, framework=framework, provider=provider, model=model,
|
|
61
|
+
agents=agent_specs, prompt_style="custom", create_venv=create_venv,
|
|
62
|
+
)
|
|
63
|
+
if enterprise:
|
|
64
|
+
spec.enable_enterprise()
|
|
65
|
+
feats = _ALL_FEATURES
|
|
66
|
+
else:
|
|
67
|
+
_apply_features(spec, feats)
|
|
68
|
+
|
|
69
|
+
target = Path(output_dir).expanduser() if output_dir else Path.cwd() / spec.slug
|
|
70
|
+
result = generate_project(spec, target, overwrite=overwrite)
|
|
71
|
+
root = result.target_dir
|
|
72
|
+
|
|
73
|
+
# Collect a compact view for the calling LLM.
|
|
74
|
+
tree = sorted(str(p.relative_to(root)) for p in root.glob("**/*") if p.is_file())
|
|
75
|
+
pkg = spec.package
|
|
76
|
+
key_paths = [
|
|
77
|
+
"pyproject.toml", "prompts.json", "agentx.json",
|
|
78
|
+
f"src/{pkg}/main.py", f"src/{pkg}/agents.py", f"src/{pkg}/prompts.py",
|
|
79
|
+
]
|
|
80
|
+
if spec.serve:
|
|
81
|
+
key_paths.append(f"src/{pkg}/server.py")
|
|
82
|
+
key_files = {}
|
|
83
|
+
for rel in key_paths:
|
|
84
|
+
fp = root / rel
|
|
85
|
+
if fp.exists():
|
|
86
|
+
key_files[rel] = fp.read_text(encoding="utf-8")[:_KEY_FILES_MAX]
|
|
87
|
+
|
|
88
|
+
run_cmd = (
|
|
89
|
+
f"uv run uvicorn {pkg}.server:app --reload" if spec.serve else f"uv run {spec.slug}"
|
|
90
|
+
)
|
|
91
|
+
next_steps = [
|
|
92
|
+
f"cd {root.name}",
|
|
93
|
+
"cp .env.example .env # add your provider API key(s)",
|
|
94
|
+
"uv venv && uv sync",
|
|
95
|
+
run_cmd,
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
"ok": True,
|
|
100
|
+
"target_dir": str(root),
|
|
101
|
+
"name": spec.slug,
|
|
102
|
+
"framework": framework,
|
|
103
|
+
"provider": provider,
|
|
104
|
+
"model": model or "(provider default)",
|
|
105
|
+
"agents": [a.name for a in agent_specs],
|
|
106
|
+
"features": feats,
|
|
107
|
+
"rationale": rec["rationale"],
|
|
108
|
+
"file_tree": tree,
|
|
109
|
+
"key_files": key_files,
|
|
110
|
+
"next_steps": next_steps,
|
|
111
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Map a natural-language problem statement to a recommended ProjectSpec.
|
|
2
|
+
|
|
3
|
+
Pure and dependency-free so it's testable and usable without the MCP SDK. The
|
|
4
|
+
connector uses this to turn a single user prompt into a complete project.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
|
|
10
|
+
_STOP = {
|
|
11
|
+
"a", "an", "the", "for", "with", "that", "this", "and", "or", "to", "of", "in",
|
|
12
|
+
"on", "is", "are", "be", "build", "create", "make", "want", "need", "should",
|
|
13
|
+
"able", "can", "will", "agent", "agents", "ai", "app", "application", "using",
|
|
14
|
+
"use", "my", "our", "me", "you", "it", "system", "assistant", "bot", "help",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
_ROLE_MAP = [
|
|
18
|
+
(("support", "ticket", "helpdesk", "customer"), "Customer Support Agent"),
|
|
19
|
+
(("research", "literature", "analyze papers", "summarize papers"), "Research Agent"),
|
|
20
|
+
(("code", "developer", "programming", "refactor", "review pull"), "Coding Assistant"),
|
|
21
|
+
(("data", "analytics", "sql", "report", "dashboard"), "Data Analyst"),
|
|
22
|
+
(("sales", "lead", "crm", "outreach"), "Sales Assistant"),
|
|
23
|
+
(("legal", "contract", "compliance", "policy"), "Compliance Assistant"),
|
|
24
|
+
(("medical", "clinical", "patient", "health"), "Clinical Assistant"),
|
|
25
|
+
(("devops", "infrastructure", "kubernetes", "deploy"), "DevOps Assistant"),
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _has(text: str, *kw: str) -> bool:
|
|
30
|
+
return any(k in text for k in kw)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _slug_from(text: str) -> str:
|
|
34
|
+
words = [w for w in re.findall(r"[a-z0-9]+", text.lower()) if w not in _STOP and len(w) > 2]
|
|
35
|
+
picked = words[:3] or ["agentx", "app"]
|
|
36
|
+
return "-".join(picked)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _role_for(text: str) -> str:
|
|
40
|
+
for keys, role in _ROLE_MAP:
|
|
41
|
+
if _has(text, *keys):
|
|
42
|
+
return role
|
|
43
|
+
return "Assistant"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def recommend_spec(problem_statement: str) -> dict:
|
|
47
|
+
"""Return a recommended spec dict + rationale for a problem statement."""
|
|
48
|
+
text = (problem_statement or "").lower().strip()
|
|
49
|
+
|
|
50
|
+
multi = _has(text, "multi-agent", "multi agent", "multiple agents", "team of",
|
|
51
|
+
"crew", "collaborat", "debate", "researcher and", "reviewer", "planner")
|
|
52
|
+
framework = "crewai" if multi else "langgraph"
|
|
53
|
+
|
|
54
|
+
rag = _has(text, "document", "knowledge", "docs", "rag", "retriev", "pdf",
|
|
55
|
+
"manual", "faq", "knowledge base", " kb", "our data", "company data",
|
|
56
|
+
"search through", "cite", "sources")
|
|
57
|
+
memory = _has(text, "remember", "conversation history", "multi-turn", "multi turn",
|
|
58
|
+
"across sessions", "previous", "chat history", "follow-up", "follow up")
|
|
59
|
+
mcp = _has(text, "mcp", "external tool", "integrate with", "third-party", "third party",
|
|
60
|
+
"plugin", "connect to", "external api")
|
|
61
|
+
skills = _has(text, "guideline", "standard", "compliance", "policy", "style guide",
|
|
62
|
+
"best practice", "framework method")
|
|
63
|
+
serve = _has(text, "api", "endpoint", "serve", "rest", "http", "backend",
|
|
64
|
+
"microservice", "webhook", "chat ui", "web app")
|
|
65
|
+
production = _has(text, "production", "enterprise", "scalable", "observability",
|
|
66
|
+
"monitor", "trace", "secure", "reliable", "deploy", "high traffic")
|
|
67
|
+
coding = _has(text, "coding", "write code", "code generation", "programming task")
|
|
68
|
+
|
|
69
|
+
features: list[str] = []
|
|
70
|
+
if rag:
|
|
71
|
+
features.append("rag")
|
|
72
|
+
if memory:
|
|
73
|
+
features.append("memory")
|
|
74
|
+
if mcp:
|
|
75
|
+
features.append("mcp")
|
|
76
|
+
if skills:
|
|
77
|
+
features.append("skills")
|
|
78
|
+
if serve or production:
|
|
79
|
+
features.append("serve")
|
|
80
|
+
if production:
|
|
81
|
+
features += ["observability", "guardrails", "docker", "ci", "evals"]
|
|
82
|
+
# de-dupe, stable order
|
|
83
|
+
seen: list[str] = []
|
|
84
|
+
for f in features:
|
|
85
|
+
if f not in seen:
|
|
86
|
+
seen.append(f)
|
|
87
|
+
|
|
88
|
+
agents = 3 if framework == "crewai" else 1
|
|
89
|
+
role = _role_for(text)
|
|
90
|
+
goal = (problem_statement or "Help the user accomplish their task accurately.").strip()
|
|
91
|
+
system_prompt = (
|
|
92
|
+
f"You are a {role}. {goal} "
|
|
93
|
+
"Be accurate and concise, use your tools/knowledge before guessing, "
|
|
94
|
+
"cite sources when available, and ask for clarification when the request is ambiguous."
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
rationale = (
|
|
98
|
+
f"Chose **{framework}** ({'multi-agent collaboration detected' if multi else 'single-agent task'}); "
|
|
99
|
+
f"features: {', '.join(seen) or 'none'}. "
|
|
100
|
+
f"{'RAG (knowledge grounding). ' if rag else ''}"
|
|
101
|
+
f"{'Serving/API layer. ' if serve or production else ''}"
|
|
102
|
+
f"{'Full enterprise pack (tracing/guardrails/docker/CI/evals). ' if production else ''}"
|
|
103
|
+
).strip()
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
"name": _slug_from(text),
|
|
107
|
+
"framework": framework,
|
|
108
|
+
"provider": "openai",
|
|
109
|
+
"model": "",
|
|
110
|
+
"agents": agents,
|
|
111
|
+
"role": role,
|
|
112
|
+
"goal": goal[:300],
|
|
113
|
+
"system_prompt": system_prompt[:600],
|
|
114
|
+
"features": seen,
|
|
115
|
+
"rationale": rationale,
|
|
116
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""MCP server exposing AgentX-Kit to Claude / Copilot / Codex as a connector.
|
|
2
|
+
|
|
3
|
+
Run over stdio with ``agentx mcp``. Any MCP-capable client (Claude Desktop,
|
|
4
|
+
Claude Code, GitHub Copilot, OpenAI Codex) can then, from a single prompt with a
|
|
5
|
+
problem statement, generate a complete, ready-to-run agent project.
|
|
6
|
+
|
|
7
|
+
Tools:
|
|
8
|
+
• recommend_project(problem_statement) → suggested stack + features
|
|
9
|
+
• create_agent_project(problem_statement, …) → generates the project, returns files
|
|
10
|
+
• list_providers() → supported LLM providers
|
|
11
|
+
• analyze_prompt(prompt) / optimize_prompt(…) → prompt insights
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from ..providers import all_specs
|
|
18
|
+
from .build import build_project_from_statement
|
|
19
|
+
from .recommend import recommend_spec
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def build_server():
|
|
23
|
+
"""Construct the FastMCP server. Requires ``agentx-kit[connector]``."""
|
|
24
|
+
try:
|
|
25
|
+
from mcp.server.fastmcp import FastMCP
|
|
26
|
+
except ImportError as exc: # pragma: no cover
|
|
27
|
+
raise RuntimeError(
|
|
28
|
+
"The MCP connector needs the MCP SDK. Install it with:\n"
|
|
29
|
+
" pip install 'agentx-kit[connector]'"
|
|
30
|
+
) from exc
|
|
31
|
+
|
|
32
|
+
mcp = FastMCP("agentx-kit")
|
|
33
|
+
|
|
34
|
+
@mcp.tool()
|
|
35
|
+
def list_providers() -> list[dict]:
|
|
36
|
+
"""List the LLM providers AgentX-Kit can target and the env vars each needs."""
|
|
37
|
+
return [
|
|
38
|
+
{"id": s.id, "label": s.label, "default_model": s.default_model, "env_vars": list(s.env_vars)}
|
|
39
|
+
for s in all_specs()
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
@mcp.tool()
|
|
43
|
+
def recommend_project(problem_statement: str) -> dict:
|
|
44
|
+
"""Recommend a framework, provider, agent count and features for a use case.
|
|
45
|
+
|
|
46
|
+
Call this first to preview the stack; then call create_agent_project.
|
|
47
|
+
"""
|
|
48
|
+
return recommend_spec(problem_statement)
|
|
49
|
+
|
|
50
|
+
@mcp.tool()
|
|
51
|
+
def create_agent_project(
|
|
52
|
+
problem_statement: str,
|
|
53
|
+
name: str = "",
|
|
54
|
+
framework: str = "",
|
|
55
|
+
provider: str = "",
|
|
56
|
+
model: str = "",
|
|
57
|
+
agents: int = 0,
|
|
58
|
+
features: list[str] | None = None,
|
|
59
|
+
enterprise: bool = False,
|
|
60
|
+
output_dir: str = "",
|
|
61
|
+
) -> dict:
|
|
62
|
+
"""Generate a complete, runnable agent project from a problem statement.
|
|
63
|
+
|
|
64
|
+
Leave optional args blank to let AgentX infer them from the problem
|
|
65
|
+
statement. ``features`` may include: rag, memory, mcp, skills,
|
|
66
|
+
observability, guardrails, serve, docker, ci, evals. Set ``enterprise``
|
|
67
|
+
true for the full production pack. Returns the target dir, file tree,
|
|
68
|
+
key file contents, and run steps.
|
|
69
|
+
"""
|
|
70
|
+
try:
|
|
71
|
+
return build_project_from_statement(
|
|
72
|
+
problem_statement, name=name, framework=framework, provider=provider,
|
|
73
|
+
model=model, agents=agents, features=features, enterprise=enterprise,
|
|
74
|
+
output_dir=output_dir, create_venv=False, overwrite=True,
|
|
75
|
+
)
|
|
76
|
+
except Exception as exc: # noqa: BLE001
|
|
77
|
+
return {"ok": False, "error": str(exc)}
|
|
78
|
+
|
|
79
|
+
@mcp.tool()
|
|
80
|
+
def analyze_prompt(prompt: str, model: str = "gpt-4o-mini") -> dict:
|
|
81
|
+
"""Analyse a prompt: token count, quality score (0-100), suggestions, warnings."""
|
|
82
|
+
from ..insights import analyze_prompt as _analyze
|
|
83
|
+
|
|
84
|
+
a = _analyze(prompt, model)
|
|
85
|
+
return {
|
|
86
|
+
"tokens": a.tokens, "quality_score": a.quality_score,
|
|
87
|
+
"checks": a.checks, "suggestions": a.suggestions, "warnings": a.warnings,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@mcp.tool()
|
|
91
|
+
def optimize_prompt(prompt: str, provider: str = "openai", model: str = "", feedback: str = "") -> dict:
|
|
92
|
+
"""Refine a prompt with an LLM (preserving intent); returns improved prompt + rationale."""
|
|
93
|
+
from ..insights import optimize_prompt as _optimize
|
|
94
|
+
|
|
95
|
+
r = _optimize(prompt, provider, model or None, feedback=feedback)
|
|
96
|
+
return {"ok": r.ok, "improved": r.improved, "rationale": r.rationale, "error": r.error}
|
|
97
|
+
|
|
98
|
+
return mcp
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def run() -> None:
|
|
102
|
+
"""Run the MCP server over stdio (for Claude/Copilot/Codex)."""
|
|
103
|
+
build_server().run()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def client_config(command: str = "agentx") -> dict[str, Any]:
|
|
107
|
+
"""Return an MCP client config snippet for this server."""
|
|
108
|
+
return {"mcpServers": {"agentx-kit": {"command": command, "args": ["mcp"]}}}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Tests for the MCP connector's recommender + project builder (no MCP transport)."""
|
|
2
|
+
import py_compile
|
|
3
|
+
|
|
4
|
+
from agentx.connector import build_project_from_statement, recommend_spec
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# ----- recommender heuristics -----
|
|
8
|
+
def test_recommend_rag_support_use_case():
|
|
9
|
+
rec = recommend_spec("Build a customer support agent that answers from our help docs and PDFs.")
|
|
10
|
+
assert rec["framework"] == "langgraph"
|
|
11
|
+
assert "rag" in rec["features"]
|
|
12
|
+
assert rec["role"] == "Customer Support Agent"
|
|
13
|
+
assert rec["name"] # non-empty slug
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_recommend_multi_agent_research():
|
|
17
|
+
rec = recommend_spec("A team of agents: a researcher and a reviewer that collaborate to write reports.")
|
|
18
|
+
assert rec["framework"] == "crewai"
|
|
19
|
+
assert rec["agents"] >= 2
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_recommend_production_api_enables_enterprise_features():
|
|
23
|
+
rec = recommend_spec("Production-ready REST API agent, scalable with observability and monitoring.")
|
|
24
|
+
for f in ("serve", "observability", "guardrails", "docker", "ci", "evals"):
|
|
25
|
+
assert f in rec["features"], f
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_recommend_minimal():
|
|
29
|
+
rec = recommend_spec("just chat with me")
|
|
30
|
+
assert rec["framework"] == "langgraph"
|
|
31
|
+
assert rec["features"] == []
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# ----- builder produces a real, compilable project -----
|
|
35
|
+
def test_build_project_from_statement(tmp_path):
|
|
36
|
+
out = build_project_from_statement(
|
|
37
|
+
"Build a support agent that answers from our documentation and serves an API.",
|
|
38
|
+
output_dir=str(tmp_path / "proj"), create_venv=False, overwrite=True,
|
|
39
|
+
)
|
|
40
|
+
assert out["ok"] is True
|
|
41
|
+
assert "rag" in out["features"] and "serve" in out["features"]
|
|
42
|
+
assert "prompts.json" in out["key_files"]
|
|
43
|
+
assert "agentx.json" in out["key_files"]
|
|
44
|
+
# the derived system prompt carries the use case
|
|
45
|
+
import json
|
|
46
|
+
prompts = json.loads(out["key_files"]["prompts.json"])
|
|
47
|
+
sp = next(iter(prompts["agents"].values()))["system_prompt"]
|
|
48
|
+
assert "documentation" in sp.lower() or "support" in sp.lower()
|
|
49
|
+
# generated python compiles
|
|
50
|
+
from pathlib import Path
|
|
51
|
+
for py in Path(out["target_dir"]).glob("**/*.py"):
|
|
52
|
+
py_compile.compile(str(py), doraise=True)
|
|
53
|
+
assert any(p.endswith("server.py") for p in out["file_tree"])
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_build_explicit_overrides(tmp_path):
|
|
57
|
+
out = build_project_from_statement(
|
|
58
|
+
"chat assistant", name="my-bot", framework="crewai", provider="ollama",
|
|
59
|
+
features=["memory"], output_dir=str(tmp_path / "o"), create_venv=False, overwrite=True,
|
|
60
|
+
)
|
|
61
|
+
assert out["name"] == "my-bot"
|
|
62
|
+
assert out["framework"] == "crewai"
|
|
63
|
+
assert "memory" in out["features"]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_client_config_shape():
|
|
67
|
+
from agentx.connector import client_config
|
|
68
|
+
|
|
69
|
+
cfg = client_config()
|
|
70
|
+
assert cfg["mcpServers"]["agentx-kit"]["args"] == ["mcp"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|