Merge branch 'feat/queen-responsibility' into feature/thinking-hook

This commit is contained in:
Timothy
2026-03-06 15:35:17 -08:00
37 changed files with 2329 additions and 6225 deletions
+2 -20
View File
@@ -1,34 +1,16 @@
{
"permissions": {
"allow": [
"mcp__agent-builder__create_session",
"mcp__agent-builder__set_goal",
"mcp__agent-builder__add_node",
"mcp__agent-builder__add_edge",
"mcp__agent-builder__configure_loop",
"mcp__agent-builder__add_mcp_server",
"mcp__agent-builder__validate_graph",
"mcp__agent-builder__export_graph",
"mcp__agent-builder__load_session_by_id",
"Bash(git status:*)",
"Bash(gh run view:*)",
"Bash(uv run:*)",
"Bash(env:*)",
"mcp__agent-builder__test_node",
"mcp__agent-builder__list_mcp_tools",
"Bash(python -m py_compile:*)",
"Bash(python -m pytest:*)",
"Bash(source:*)",
"mcp__agent-builder__update_node",
"mcp__agent-builder__check_missing_credentials",
"mcp__agent-builder__list_stored_credentials",
"Bash(find:*)",
"mcp__agent-builder__run_tests",
"Bash(PYTHONPATH=core:exports:tools/src uv run pytest:*)",
"mcp__agent-builder__list_agent_sessions",
"mcp__agent-builder__generate_constraint_tests",
"mcp__agent-builder__generate_success_tests"
"Bash(PYTHONPATH=core:exports:tools/src uv run pytest:*)"
]
},
"enabledMcpjsonServers": ["agent-builder", "tools"]
"enabledMcpjsonServers": ["tools"]
}
-2
View File
@@ -67,8 +67,6 @@ temp/
exports/*
.agent-builder-sessions/*
.claude/settings.local.json
.claude/skills/ship-it/
+1 -7
View File
@@ -1,9 +1,3 @@
{
"mcpServers": {
"agent-builder": {
"command": "uv",
"args": ["run", "-m", "framework.mcp.agent_builder_server"],
"cwd": "core"
}
}
"mcpServers": {}
}
-1
View File
@@ -1,5 +1,4 @@
exports/
docs/
.agent-builder-sessions/
.pytest_cache/
**/__pycache__/
-5
View File
@@ -1,10 +1,5 @@
{
"mcpServers": {
"agent-builder": {
"command": "python",
"args": ["-m", "framework.mcp.agent_builder_server"],
"cwd": "core"
},
"tools": {
"command": "python",
"args": ["-m", "aden_tools.mcp_server", "--stdio"],
+10 -11
View File
@@ -1,17 +1,16 @@
# MCP Server Guide - Agent Builder
# MCP Server Guide - Agent Building Tools
This guide covers the MCP (Model Context Protocol) server for building goal-driven agents.
> **Note:** The standalone `agent-builder` MCP server (`framework.mcp.agent_builder_server`) has been replaced. Agent building is now done via the `coder-tools` server's `initialize_agent_package` tool, with underlying logic in `framework.builder.package_generator`.
This guide covers the MCP tools available for building goal-driven agents.
## Setup
### Quick Setup
```bash
# Using the setup script (recommended)
python setup_mcp.py
# Or using bash
./setup_mcp.sh
# Run the quickstart script (recommended)
./quickstart.sh
```
### Manual Configuration
@@ -21,10 +20,10 @@ Add to your MCP client configuration (e.g., Claude Desktop):
```json
{
"mcpServers": {
"agent-builder": {
"command": "python",
"args": ["-m", "framework.mcp.agent_builder_server"],
"cwd": "/path/to/goal-agent"
"coder-tools": {
"command": "uv",
"args": ["run", "coder_tools_server.py", "--stdio"],
"cwd": "/path/to/hive/tools"
}
}
}
+3 -58
View File
@@ -17,66 +17,11 @@ Framework provides a runtime framework that captures **decisions**, not just act
uv pip install -e .
```
## MCP Server Setup
## Agent Building
The framework includes an MCP (Model Context Protocol) server for building agents. To set up the MCP server:
Agent scaffolding is handled by the `coder-tools` MCP server (in `tools/coder_tools_server.py`), which provides the `initialize_agent_package` tool and related utilities. The underlying package generation logic lives in `framework.builder.package_generator`.
### Automated Setup
**Using bash (Linux/macOS):**
```bash
./setup_mcp.sh
```
**Using Python (cross-platform):**
```bash
python setup_mcp.py
```
The setup script will:
1. Install the framework package
2. Install MCP dependencies (mcp, fastmcp)
3. Create/verify `.mcp.json` configuration
4. Test the MCP server module
### Manual Setup
If you prefer manual setup:
```bash
# Install framework
uv pip install -e .
# Install MCP dependencies
uv pip install mcp fastmcp
# Test the server
uv run python -m framework.mcp.agent_builder_server
```
### Using with MCP Clients
To use the agent builder with Claude Desktop or other MCP clients, add this to your MCP client configuration:
```json
{
"mcpServers": {
"agent-builder": {
"command": "python",
"args": ["-m", "framework.mcp.agent_builder_server"],
"cwd": "/path/to/hive/core"
}
}
}
```
The MCP server provides tools for:
- Creating agent building sessions
- Defining goals with success criteria
- Adding nodes (event_loop only)
- Connecting nodes with edges
- Validating and exporting agent graphs
- Testing nodes and full agent graphs
See the [Getting Started Guide](../docs/getting-started.md) for building agents.
## Quick Start
-75
View File
@@ -95,81 +95,6 @@ async def example_3_config_file():
(test_agent_path / "mcp_servers.json").unlink()
async def example_4_custom_agent_with_mcp_tools():
"""Example 4: Build custom agent that uses MCP tools"""
print("\n=== Example 4: Custom Agent with MCP Tools ===\n")
from framework.builder.workflow import GraphBuilder
# Create a workflow builder
builder = GraphBuilder()
# Define goal
builder.set_goal(
goal_id="web-researcher",
name="Web Research Agent",
description="Search the web and summarize findings",
)
# Add success criteria
builder.add_success_criterion(
"search-results", "Successfully retrieve at least 3 web search results"
)
builder.add_success_criterion("summary", "Provide a clear, concise summary of the findings")
# Add nodes that will use MCP tools
builder.add_node(
node_id="web-searcher",
name="Web Search",
description="Search the web for information",
node_type="event_loop",
system_prompt="Search for {query} and return the top results. Use the web_search tool.",
tools=["web_search"], # This tool comes from tools MCP server
input_keys=["query"],
output_keys=["search_results"],
)
builder.add_node(
node_id="summarizer",
name="Summarize Results",
description="Summarize the search results",
node_type="event_loop",
system_prompt="Summarize the following search results in 2-3 sentences: {search_results}",
input_keys=["search_results"],
output_keys=["summary"],
)
# Connect nodes
builder.add_edge("web-searcher", "summarizer")
# Set entry point
builder.set_entry("web-searcher")
builder.set_terminal("summarizer")
# Export the agent
export_path = Path("exports/web-research-agent")
export_path.mkdir(parents=True, exist_ok=True)
builder.export(export_path)
# Load and register MCP server
runner = AgentRunner.load(export_path)
runner.register_mcp_server(
name="tools",
transport="stdio",
command="python",
args=["-m", "aden_tools.mcp_server", "--stdio"],
cwd="../tools",
)
# Run the agent
result = await runner.run({"query": "latest AI breakthroughs 2026"})
print(f"\nAgent completed with result:\n{result}")
# Cleanup
runner.cleanup()
async def main():
"""Run all examples"""
print("=" * 60)
+1 -1
View File
@@ -7,7 +7,7 @@ from .nodes import coder_node, queen_node
# Goal definition
goal = Goal(
id="agent-builder",
id="hive-coder",
name="Hive Agent Builder",
description=(
"Build complete, validated Hive agent packages from natural language "
@@ -113,7 +113,7 @@ _QUEEN_RUNNING_TOOLS = [
# additions.
# ---------------------------------------------------------------------------
_agent_builder_knowledge = """\
_package_builder_knowledge = """\
**A responsible engineer doesn't jump into building. First, \
understand the problem and be transparent about what the framework can and cannot do.**
@@ -880,7 +880,7 @@ coder_node = NodeSpec(
system_prompt=(
"You are Hive Coder, the best agent-building coding agent. You build "
"production-ready Hive agent packages from natural language.\n"
+ _agent_builder_knowledge
+ _package_builder_knowledge
+ _coder_completion
+ _appendices
),
@@ -962,7 +962,7 @@ queen_node = NodeSpec(
system_prompt=(
_queen_identity_building
+ _queen_style
+ _agent_builder_knowledge
+ _package_builder_knowledge
+ _gcu_building_section # GCU as first-class citizen (not appendix)
+ _queen_tools_docs
+ _queen_behavior
@@ -995,7 +995,7 @@ __all__ = [
"_queen_behavior_running",
"_queen_phase_7",
"_queen_style",
"_agent_builder_knowledge",
"_package_builder_knowledge",
"_appendices",
"_gcu_building_section",
]
-14
View File
@@ -1,21 +1,7 @@
"""Builder interface for analyzing and building agents."""
from framework.builder.query import BuilderQuery
from framework.builder.workflow import (
BuildPhase,
BuildSession,
GraphBuilder,
TestCase,
TestResult,
ValidationResult,
)
__all__ = [
"BuilderQuery",
"GraphBuilder",
"BuildSession",
"BuildPhase",
"ValidationResult",
"TestCase",
"TestResult",
]
File diff suppressed because it is too large Load Diff
-832
View File
@@ -1,832 +0,0 @@
"""
GraphBuilder Workflow - Enforced incremental building with HITL approval.
The build process:
1. Define Goal APPROVE
2. Add Node VALIDATE TEST APPROVE
3. Add Edge VALIDATE TEST APPROVE
4. Repeat until graph is complete
5. Final integration test APPROVE
6. Export
Each step requires validation and human approval before proceeding.
You cannot skip steps or bypass validation.
"""
from collections.abc import Callable
from datetime import datetime
from enum import StrEnum
from pathlib import Path
from typing import Any
from pydantic import BaseModel, Field
from framework.graph.edge import EdgeCondition, EdgeSpec, GraphSpec
from framework.graph.goal import Goal
from framework.graph.node import NodeSpec
class BuildPhase(StrEnum):
"""Current phase of the build process."""
INIT = "init" # Just started
GOAL_DRAFT = "goal_draft" # Drafting goal
GOAL_APPROVED = "goal_approved" # Goal approved
ADDING_NODES = "adding_nodes" # Adding nodes
ADDING_EDGES = "adding_edges" # Adding edges
TESTING = "testing" # Running tests
APPROVED = "approved" # Fully approved
EXPORTED = "exported" # Exported to file
class ValidationResult(BaseModel):
"""Result of a validation check."""
valid: bool
errors: list[str] = Field(default_factory=list)
warnings: list[str] = Field(default_factory=list)
suggestions: list[str] = Field(default_factory=list)
class TestCase(BaseModel):
"""A test case for validating agent behavior."""
id: str
description: str
input: dict[str, Any]
expected_output: Any = None # None means just check it doesn't error
expected_contains: str | None = None
class TestResult(BaseModel):
"""Result of running a test case."""
test_id: str
passed: bool
actual_output: Any = None
error: str | None = None
execution_path: list[str] = Field(default_factory=list)
class BuildSession(BaseModel):
"""
Persistent build session state.
Saved after each approved step so you can resume later.
"""
id: str
name: str
phase: BuildPhase = BuildPhase.INIT
created_at: datetime = Field(default_factory=datetime.now)
updated_at: datetime = Field(default_factory=datetime.now)
# The artifacts being built
goal: Goal | None = None
nodes: list[NodeSpec] = Field(default_factory=list)
edges: list[EdgeSpec] = Field(default_factory=list)
# Test cases
test_cases: list[TestCase] = Field(default_factory=list)
test_results: list[TestResult] = Field(default_factory=list)
# Approval history
approvals: list[dict[str, Any]] = Field(default_factory=list)
# Tools (stored as dicts for serialization)
tools: list[dict[str, Any]] = Field(default_factory=list)
model_config = {"extra": "allow"}
class GraphBuilder:
"""
Enforced incremental graph building with HITL approval.
Usage:
builder = GraphBuilder("my-agent")
# Step 1: Define and approve goal
builder.set_goal(goal)
builder.validate() # Must pass
builder.approve("Goal looks good") # Human approval required
# Step 2: Add nodes one by one
builder.add_node(node_spec)
builder.validate() # Must pass
builder.test(test_case) # Must pass
builder.approve("Node works")
# Step 3: Add edges
builder.add_edge(edge_spec)
builder.validate()
builder.approve("Edge correct")
# Step 4: Final approval
builder.run_all_tests()
builder.final_approve("Ready for production")
# Step 5: Export
graph = builder.export()
"""
def __init__(
self,
name: str,
storage_path: Path | str | None = None,
session_id: str | None = None,
):
self.storage_path = Path(storage_path) if storage_path else Path.home() / ".core" / "builds"
self.storage_path.mkdir(parents=True, exist_ok=True)
if session_id:
self.session = self._load_session(session_id)
else:
self.session = BuildSession(
id=f"build_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
name=name,
)
self._pending_validation: ValidationResult | None = None
# =========================================================================
# PHASE 1: GOAL
# =========================================================================
def set_goal(self, goal: Goal) -> ValidationResult:
"""
Set the goal for this agent.
Returns validation result. Must call approve() after validation passes.
"""
self._require_phase([BuildPhase.INIT, BuildPhase.GOAL_DRAFT])
self.session.goal = goal
self.session.phase = BuildPhase.GOAL_DRAFT
validation = self._validate_goal(goal)
self._pending_validation = validation
self._save_session()
return validation
def _validate_goal(self, goal: Goal) -> ValidationResult:
"""Validate a goal definition."""
errors = []
warnings = []
suggestions = []
if not goal.id:
errors.append("Goal must have an id")
if not goal.name:
errors.append("Goal must have a name")
if not goal.description:
errors.append("Goal must have a description")
if not goal.success_criteria:
errors.append("Goal must have at least one success criterion")
else:
for sc in goal.success_criteria:
if not sc.description:
errors.append(f"Success criterion '{sc.id}' needs a description")
if not goal.constraints:
warnings.append("Consider adding constraints to define boundaries")
if not goal.required_capabilities:
suggestions.append("Specify required_capabilities (e.g., ['llm', 'tools'])")
return ValidationResult(
valid=len(errors) == 0,
errors=errors,
warnings=warnings,
suggestions=suggestions,
)
# =========================================================================
# PHASE 2: NODES
# =========================================================================
def add_node(self, node: NodeSpec) -> ValidationResult:
"""
Add a node to the graph.
Returns validation result. Must call approve() after validation passes.
"""
self._require_phase([BuildPhase.GOAL_APPROVED, BuildPhase.ADDING_NODES])
# Check for duplicate
if any(n.id == node.id for n in self.session.nodes):
return ValidationResult(
valid=False,
errors=[f"Node with id '{node.id}' already exists"],
)
self.session.nodes.append(node)
self.session.phase = BuildPhase.ADDING_NODES
validation = self._validate_node(node)
self._pending_validation = validation
self._save_session()
return validation
def _validate_node(self, node: NodeSpec) -> ValidationResult:
"""Validate a node definition."""
errors = []
warnings = []
suggestions = []
if not node.id:
errors.append("Node must have an id")
if not node.name:
errors.append("Node must have a name")
if not node.description:
warnings.append(f"Node '{node.id}' should have a description")
# Type-specific validation
if node.node_type == "event_loop":
if node.tools and not node.system_prompt:
warnings.append(f"Event loop node '{node.id}' should have a system_prompt")
if node.node_type == "router":
if not node.routes:
errors.append(f"Router node '{node.id}' must specify routes")
# Check input/output keys
if not node.input_keys:
suggestions.append(f"Consider specifying input_keys for '{node.id}'")
if not node.output_keys:
suggestions.append(f"Consider specifying output_keys for '{node.id}'")
return ValidationResult(
valid=len(errors) == 0,
errors=errors,
warnings=warnings,
suggestions=suggestions,
)
def update_node(self, node_id: str, **updates) -> ValidationResult:
"""Update an existing node."""
self._require_phase([BuildPhase.ADDING_NODES])
for i, node in enumerate(self.session.nodes):
if node.id == node_id:
node_dict = node.model_dump()
node_dict.update(updates)
updated_node = NodeSpec(**node_dict)
self.session.nodes[i] = updated_node
validation = self._validate_node(updated_node)
self._pending_validation = validation
self._save_session()
return validation
return ValidationResult(valid=False, errors=[f"Node '{node_id}' not found"])
def remove_node(self, node_id: str) -> ValidationResult:
"""Remove a node (only if no edges reference it)."""
self._require_phase([BuildPhase.ADDING_NODES])
# Check for edge references
for edge in self.session.edges:
if edge.source == node_id or edge.target == node_id:
return ValidationResult(
valid=False,
errors=[f"Cannot remove node '{node_id}': referenced by edge '{edge.id}'"],
)
self.session.nodes = [n for n in self.session.nodes if n.id != node_id]
self._save_session()
return ValidationResult(valid=True)
# =========================================================================
# PHASE 3: EDGES
# =========================================================================
def add_edge(self, edge: EdgeSpec) -> ValidationResult:
"""
Add an edge to the graph.
Returns validation result. Must call approve() after validation passes.
"""
self._require_phase([BuildPhase.ADDING_NODES, BuildPhase.ADDING_EDGES])
# Check for duplicate
if any(e.id == edge.id for e in self.session.edges):
return ValidationResult(
valid=False,
errors=[f"Edge with id '{edge.id}' already exists"],
)
self.session.edges.append(edge)
self.session.phase = BuildPhase.ADDING_EDGES
validation = self._validate_edge(edge)
self._pending_validation = validation
self._save_session()
return validation
def _validate_edge(self, edge: EdgeSpec) -> ValidationResult:
"""Validate an edge definition."""
errors = []
warnings = []
if not edge.id:
errors.append("Edge must have an id")
# Check source exists
if not any(n.id == edge.source for n in self.session.nodes):
errors.append(f"Edge source '{edge.source}' not found in nodes")
# Check target exists
if not any(n.id == edge.target for n in self.session.nodes):
errors.append(f"Edge target '{edge.target}' not found in nodes")
# Warn about conditional edges without expressions
if edge.condition == EdgeCondition.CONDITIONAL and not edge.condition_expr:
warnings.append(f"Conditional edge '{edge.id}' has no condition_expr")
return ValidationResult(
valid=len(errors) == 0,
errors=errors,
warnings=warnings,
)
# =========================================================================
# VALIDATION & TESTING
# =========================================================================
def validate(self) -> ValidationResult:
"""Validate the entire current graph state."""
errors = []
warnings = []
# Must have a goal
if not self.session.goal:
errors.append("No goal defined")
return ValidationResult(valid=False, errors=errors)
# Must have at least one node
if not self.session.nodes:
errors.append("No nodes defined")
# Check for entry node
entry_candidates = []
for node in self.session.nodes:
# A node is an entry candidate if no edges point to it
if not any(e.target == node.id for e in self.session.edges):
entry_candidates.append(node.id)
if len(entry_candidates) == 0 and self.session.nodes:
errors.append("No entry node found (all nodes have incoming edges)")
elif len(entry_candidates) > 1:
warnings.append(f"Multiple entry candidates: {entry_candidates}. Specify one.")
# Check for terminal nodes
terminal_candidates = []
for node in self.session.nodes:
if not any(e.source == node.id for e in self.session.edges):
terminal_candidates.append(node.id)
if not terminal_candidates and self.session.nodes:
warnings.append("No terminal nodes found (all nodes have outgoing edges)")
# Check reachability from ALL entry candidates (not just the first one).
# Agents with async entry points have multiple nodes with no incoming
# edges (e.g., a primary entry node and an event-driven entry node).
if entry_candidates and self.session.nodes:
reachable = set()
for candidate in entry_candidates:
reachable |= self._compute_reachable(candidate)
unreachable = [n.id for n in self.session.nodes if n.id not in reachable]
if unreachable:
errors.append(f"Unreachable nodes: {unreachable}")
validation = ValidationResult(
valid=len(errors) == 0,
errors=errors,
warnings=warnings,
)
self._pending_validation = validation
return validation
def _compute_reachable(self, start: str) -> set[str]:
"""Compute all nodes reachable from start."""
reachable = set()
to_visit = [start]
while to_visit:
current = to_visit.pop()
if current in reachable:
continue
reachable.add(current)
for edge in self.session.edges:
if edge.source == current:
to_visit.append(edge.target)
# Also follow router routes
for node in self.session.nodes:
if node.id == current and node.routes:
for target in node.routes.values():
to_visit.append(target)
return reachable
def add_test(self, test: TestCase) -> None:
"""Add a test case."""
self.session.test_cases.append(test)
self._save_session()
async def run_test_async(
self,
test: TestCase,
executor_factory: Callable,
) -> TestResult:
"""
Run a single test case asynchronously.
This method is safe to call from async contexts (Jupyter, FastAPI, etc.).
executor_factory should return a configured GraphExecutor.
"""
self._require_phase([BuildPhase.ADDING_NODES, BuildPhase.ADDING_EDGES, BuildPhase.TESTING])
self.session.phase = BuildPhase.TESTING
try:
# Build temporary graph for testing
graph = self._build_graph()
executor = executor_factory()
# Run the test
result = await executor.execute(
graph=graph,
goal=self.session.goal,
input_data=test.input,
)
# Check result
passed = result.success
if test.expected_output is not None:
passed = passed and (result.output.get("result") == test.expected_output)
if test.expected_contains:
output_str = str(result.output)
passed = passed and (test.expected_contains in output_str)
test_result = TestResult(
test_id=test.id,
passed=passed,
actual_output=result.output,
execution_path=result.path,
)
except Exception as e:
test_result = TestResult(
test_id=test.id,
passed=False,
error=str(e),
)
self.session.test_results.append(test_result)
self._save_session()
return test_result
def run_test(
self,
test: TestCase,
executor_factory: Callable,
) -> TestResult:
"""
Run a single test case.
This is a synchronous wrapper around run_test_async().
If called from an async context (Jupyter, FastAPI, etc.), use run_test_async() instead.
executor_factory should return a configured GraphExecutor.
"""
import asyncio
# Check if an event loop is already running
# get_running_loop() returns a loop if one exists, or raises RuntimeError if none exists
try:
asyncio.get_running_loop()
except RuntimeError:
# No event loop running - safe to use asyncio.run()
return asyncio.run(self.run_test_async(test, executor_factory))
# Event loop is running - cannot use asyncio.run()
raise RuntimeError(
"Cannot call run_test() from an async context. "
"An event loop is already running. "
"Please use 'await builder.run_test_async(test, executor_factory)' instead."
)
def run_all_tests(self, executor_factory: Callable) -> list[TestResult]:
"""Run all test cases."""
results = []
for test in self.session.test_cases:
result = self.run_test(test, executor_factory)
results.append(result)
return results
# =========================================================================
# APPROVAL
# =========================================================================
def approve(self, comment: str) -> bool:
"""
Approve the current pending change.
Must have a passing validation to approve.
Returns True if approved, False if validation failed.
"""
if self._pending_validation is None:
raise RuntimeError("Nothing to approve. Run validation first.")
if not self._pending_validation.valid:
return False
self.session.approvals.append(
{
"phase": self.session.phase.value,
"comment": comment,
"timestamp": datetime.now().isoformat(),
"validation": self._pending_validation.model_dump(),
}
)
# Advance phase if appropriate
if self.session.phase == BuildPhase.GOAL_DRAFT:
self.session.phase = BuildPhase.GOAL_APPROVED
self._pending_validation = None
self._save_session()
return True
def final_approve(self, comment: str) -> bool:
"""
Final approval for the complete graph.
Requires all tests to pass.
"""
# Run final validation
validation = self.validate()
if not validation.valid:
self._pending_validation = validation
return False
# Check test results
if self.session.test_cases:
failed_tests = [t for t in self.session.test_results if not t.passed]
if failed_tests:
self._pending_validation = ValidationResult(
valid=False,
errors=[f"Failed tests: {[t.test_id for t in failed_tests]}"],
)
return False
self.session.phase = BuildPhase.APPROVED
self.session.approvals.append(
{
"phase": "final",
"comment": comment,
"timestamp": datetime.now().isoformat(),
}
)
self._save_session()
return True
# =========================================================================
# EXPORT
# =========================================================================
def export(self) -> GraphSpec:
"""
Export the approved graph.
Requires final approval.
"""
self._require_phase([BuildPhase.APPROVED])
graph = self._build_graph()
self.session.phase = BuildPhase.EXPORTED
self._save_session()
return graph
def _build_graph(self) -> GraphSpec:
"""Build a GraphSpec from current session."""
# Determine entry node
entry_node = None
for node in self.session.nodes:
if not any(e.target == node.id for e in self.session.edges):
entry_node = node.id
break
# Determine terminal nodes
terminal_nodes = []
for node in self.session.nodes:
if not any(e.source == node.id for e in self.session.edges):
terminal_nodes.append(node.id)
# Collect all memory keys
memory_keys = set()
for node in self.session.nodes:
memory_keys.update(node.input_keys)
memory_keys.update(node.output_keys)
return GraphSpec(
id=f"{self.session.name}-graph",
goal_id=self.session.goal.id if self.session.goal else "",
entry_node=entry_node or "",
terminal_nodes=terminal_nodes,
nodes=self.session.nodes,
edges=self.session.edges,
memory_keys=list(memory_keys),
)
def export_to_file(self, path: Path | str) -> None:
"""Export the graph to a Python file."""
self._require_phase([BuildPhase.APPROVED, BuildPhase.EXPORTED])
graph = self._build_graph()
# Generate Python code
code = self._generate_code(graph)
Path(path).write_text(code, encoding="utf-8")
self.session.phase = BuildPhase.EXPORTED
self._save_session()
def _generate_code(self, graph: GraphSpec) -> str:
"""Generate Python code for the graph."""
lines = [
'"""',
f"Generated agent: {self.session.name}",
f"Generated at: {datetime.now().isoformat()}",
'"""',
"",
"from framework.graph import (",
" Goal, SuccessCriterion, Constraint,",
" NodeSpec, EdgeSpec, EdgeCondition,",
")",
"from framework.graph.edge import GraphSpec",
"from framework.graph.goal import GoalStatus",
"",
"",
"# Goal",
]
if self.session.goal:
goal_json = self.session.goal.model_dump_json(indent=4)
lines.append("GOAL = Goal.model_validate_json('''")
lines.append(goal_json)
lines.append("''')")
else:
lines.append("GOAL = None")
lines.extend(
[
"",
"",
"# Nodes",
"NODES = [",
]
)
for node in self.session.nodes:
node_json = node.model_dump_json(indent=4)
lines.append(" NodeSpec.model_validate_json('''")
lines.append(node_json)
lines.append(" '''),")
lines.extend(
[
"]",
"",
"",
"# Edges",
"EDGES = [",
]
)
for edge in self.session.edges:
edge_json = edge.model_dump_json(indent=4)
lines.append(" EdgeSpec.model_validate_json('''")
lines.append(edge_json)
lines.append(" '''),")
lines.extend(
[
"]",
"",
"",
"# Graph",
]
)
graph_json = graph.model_dump_json(indent=4)
lines.append("GRAPH = GraphSpec.model_validate_json('''")
lines.append(graph_json)
lines.append("''')")
return "\n".join(lines)
# =========================================================================
# SESSION MANAGEMENT
# =========================================================================
def _require_phase(self, allowed: list[BuildPhase]) -> None:
"""Ensure we're in an allowed phase."""
if self.session.phase not in allowed:
raise RuntimeError(
f"Cannot perform this action in phase '{self.session.phase.value}'. "
f"Allowed phases: {[p.value for p in allowed]}"
)
def _save_session(self) -> None:
"""Save session to disk."""
self.session.updated_at = datetime.now()
path = self.storage_path / f"{self.session.id}.json"
path.write_text(self.session.model_dump_json(indent=2), encoding="utf-8")
def _load_session(self, session_id: str) -> BuildSession:
"""Load session from disk."""
path = self.storage_path / f"{session_id}.json"
if not path.exists():
raise FileNotFoundError(f"Session not found: {session_id}")
return BuildSession.model_validate_json(path.read_text(encoding="utf-8"))
@classmethod
def list_sessions(cls, storage_path: Path | str | None = None) -> list[str]:
"""List all saved sessions."""
path = Path(storage_path) if storage_path else Path.home() / ".core" / "builds"
if not path.exists():
return []
return [f.stem for f in path.glob("*.json")]
# =========================================================================
# STATUS
# =========================================================================
def status(self) -> dict[str, Any]:
"""Get current build status."""
return {
"session_id": self.session.id,
"name": self.session.name,
"phase": self.session.phase.value,
"goal": self.session.goal.name if self.session.goal else None,
"nodes": len(self.session.nodes),
"edges": len(self.session.edges),
"tests": len(self.session.test_cases),
"tests_passed": sum(1 for t in self.session.test_results if t.passed),
"approvals": len(self.session.approvals),
"pending_validation": self._pending_validation.model_dump()
if self._pending_validation
else None,
}
def show(self) -> str:
"""Show current graph as text."""
lines = [
f"=== Build: {self.session.name} ===",
f"Phase: {self.session.phase.value}",
"",
]
if self.session.goal:
lines.extend(
[
f"Goal: {self.session.goal.name}",
f" {self.session.goal.description}",
"",
]
)
if self.session.nodes:
lines.append("Nodes:")
for node in self.session.nodes:
lines.append(f" [{node.id}] {node.name} ({node.node_type})")
lines.append("")
if self.session.edges:
lines.append("Edges:")
for edge in self.session.edges:
lines.append(f" {edge.source} --{edge.condition.value}--> {edge.target}")
lines.append("")
if self._pending_validation:
lines.append("Pending Validation:")
lines.append(f" Valid: {self._pending_validation.valid}")
for err in self._pending_validation.errors:
lines.append(f" ERROR: {err}")
for warn in self._pending_validation.warnings:
lines.append(f" WARN: {warn}")
return "\n".join(lines)
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -560,7 +560,7 @@ class SessionManager:
_QUEEN_BUILDING_TOOLS,
_QUEEN_RUNNING_TOOLS,
_QUEEN_STAGING_TOOLS,
_agent_builder_knowledge,
_package_builder_knowledge,
_appendices,
_queen_behavior_always,
_queen_behavior_building,
@@ -613,7 +613,7 @@ class SessionManager:
+ _queen_tools_building
+ _queen_behavior_always
+ _queen_behavior_building
+ _agent_builder_knowledge
+ _package_builder_knowledge
+ _queen_phase_7
+ _appendices
+ worker_identity
+1 -1
View File
@@ -19,7 +19,7 @@ then run with pytest and debugged with LLM assistance.
## MCP Tools
Testing tools are integrated into the main agent_builder_server.py:
Testing tools are available via the package generator:
- generate_constraint_tests, generate_success_tests (return guidelines)
- run_tests, debug_test, list_tests
+10 -46
View File
@@ -7,7 +7,6 @@ This script installs the framework and configures the MCP server.
import json
import logging
import os
import subprocess
import sys
from pathlib import Path
@@ -75,12 +74,12 @@ def main():
# Get script directory
script_dir = Path(__file__).parent.absolute()
os.chdir(script_dir)
# Step 1: Install framework package
log_step("Step 1: Installing framework package...")
if not run_command(
[sys.executable, "-m", "pip", "install", "-e", "."], "Failed to install framework package"
[sys.executable, "-m", "pip", "install", "-e", str(script_dir)],
"Failed to install framework package",
):
sys.exit(1)
log_success("Framework package installed")
@@ -96,7 +95,7 @@ def main():
log_success("MCP dependencies installed")
logger.info("")
# Step 3: Verify/create MCP configuration
# Step 3: Verify MCP configuration
log_step("Step 3: Verifying MCP server configuration...")
mcp_config_path = script_dir / ".mcp.json"
@@ -107,39 +106,22 @@ def main():
config = json.load(f)
logger.info(json.dumps(config, indent=2))
else:
log_error("No .mcp.json found")
logger.info("Creating default MCP configuration...")
config = {
"mcpServers": {
"agent-builder": {
"command": "python",
"args": ["-m", "framework.mcp.agent_builder_server"],
"cwd": str(script_dir),
}
}
}
with open(mcp_config_path, "w", encoding="utf-8") as f:
json.dump(config, f, indent=2)
log_success("Created .mcp.json")
log_success("No .mcp.json needed (MCP servers configured at repo root)")
logger.info("")
# Step 4: Test MCP server
log_step("Step 4: Testing MCP server...")
# Step 4: Test framework import
log_step("Step 4: Testing framework import...")
try:
# Try importing the MCP server module
subprocess.run(
[sys.executable, "-c", "from framework.mcp import agent_builder_server"],
[sys.executable, "-c", "import framework; print('OK')"],
check=True,
capture_output=True,
text=True,
encoding="utf-8",
)
log_success("MCP server module verified")
log_success("Framework module verified")
except subprocess.CalledProcessError as e:
log_error("Failed to import MCP server module")
log_error("Failed to import framework module")
logger.error(f"Error: {e.stderr}")
sys.exit(1)
logger.info("")
@@ -147,29 +129,11 @@ def main():
# Success summary
logger.info(f"{Colors.GREEN}=== Setup Complete ==={Colors.NC}")
logger.info("")
logger.info("The MCP server is now ready to use!")
logger.info("")
logger.info(f"{Colors.BLUE}To start the MCP server manually:{Colors.NC}")
logger.info(" uv run python -m framework.mcp.agent_builder_server")
logger.info("The framework is now ready to use!")
logger.info("")
logger.info(f"{Colors.BLUE}MCP Configuration location:{Colors.NC}")
logger.info(f" {mcp_config_path}")
logger.info("")
logger.info(f"{Colors.BLUE}To use with Claude Desktop or other MCP clients,{Colors.NC}")
logger.info(f"{Colors.BLUE}add the following to your MCP client configuration:{Colors.NC}")
logger.info("")
example_config = {
"mcpServers": {
"agent-builder": {
"command": "python",
"args": ["-m", "framework.mcp.agent_builder_server"],
"cwd": str(script_dir),
}
}
}
logger.info(json.dumps(example_config, indent=2))
logger.info("")
if __name__ == "__main__":
+6 -36
View File
@@ -40,52 +40,22 @@ if [ -f ".mcp.json" ]; then
echo "Configuration:"
cat .mcp.json
else
echo -e "${RED} No .mcp.json found${NC}"
echo "Creating default MCP configuration..."
cat > .mcp.json <<EOF
{
"mcpServers": {
"agent-builder": {
"command": "python",
"args": ["-m", "framework.mcp.agent_builder_server"],
"cwd": "$SCRIPT_DIR"
}
}
}
EOF
echo -e "${GREEN}✓ Created .mcp.json${NC}"
echo -e "${GREEN} No .mcp.json needed (MCP servers configured at repo root)${NC}"
fi
echo ""
echo -e "${YELLOW}Step 4: Testing MCP server...${NC}"
uv run python -c "from framework.mcp import agent_builder_server; print('✓ MCP server module loads successfully')" || {
echo -e "${RED}Failed to import MCP server module${NC}"
echo -e "${YELLOW}Step 4: Testing framework import...${NC}"
uv run python -c "import framework; print('✓ Framework module loads successfully')" || {
echo -e "${RED}Failed to import framework module${NC}"
exit 1
}
echo -e "${GREEN}MCP server module verified${NC}"
echo -e "${GREEN}Framework module verified${NC}"
echo ""
echo -e "${GREEN}=== Setup Complete ===${NC}"
echo ""
echo "The MCP server is now ready to use!"
echo ""
echo "To start the MCP server manually:"
echo " uv run python -m framework.mcp.agent_builder_server"
echo "The framework is now ready to use!"
echo ""
echo "MCP Configuration location:"
echo " $SCRIPT_DIR/.mcp.json"
echo ""
echo "To use with Claude Desktop or other MCP clients,"
echo "add the following to your MCP client configuration:"
echo ""
echo "{
\"mcpServers\": {
\"agent-builder\": {
\"command\": \"python\",
\"args\": [\"-m\", \"framework.mcp.agent_builder_server\"],
\"cwd\": \"$SCRIPT_DIR\"
}
}
}"
echo ""
@@ -1,61 +0,0 @@
"""Worker-autonomy guardrails for agent_builder_server."""
import json
from framework.graph import Goal, NodeSpec
from framework.mcp import agent_builder_server as builder
def _make_session(name: str = "autonomy_test"):
session = builder.BuildSession(name=name)
session.goal = Goal(
id="g1",
name="Autonomy Goal",
description="Workers stay autonomous.",
success_criteria=[],
constraints=[],
)
return session
def test_add_node_rejects_client_facing_event_loop(monkeypatch):
session = _make_session()
monkeypatch.setattr(builder, "_session", session)
monkeypatch.setattr(builder, "_save_session", lambda _: None)
raw = builder.add_node(
node_id="worker",
name="Worker",
description="Autonomous worker node",
node_type="event_loop",
input_keys='["task"]',
output_keys='["result"]',
system_prompt="Do work.",
client_facing=True,
)
data = json.loads(raw)
assert data["valid"] is False
assert any("client_facing=True" in err for err in data["errors"])
def test_validate_graph_rejects_client_facing_event_loop(monkeypatch):
session = _make_session()
session.nodes = [
NodeSpec(
id="worker",
name="Worker",
description="Autonomous worker node",
node_type="event_loop",
client_facing=True,
input_keys=[],
output_keys=[],
system_prompt="Do work.",
)
]
monkeypatch.setattr(builder, "_session", session)
data = json.loads(builder.validate_graph())
assert data["valid"] is False
assert any("must not be client_facing" in err for err in data["errors"])
-46
View File
@@ -42,40 +42,6 @@ class TestMCPDependencies:
assert FastMCP is not None
class TestAgentBuilderServerModule:
"""Tests for the agent_builder_server module."""
def test_module_importable(self):
"""Test that framework.mcp.agent_builder_server can be imported."""
if not MCP_AVAILABLE:
pytest.skip(MCP_SKIP_REASON)
import framework.mcp.agent_builder_server as module
assert module is not None
def test_mcp_object_exported(self):
"""Test that the module exports the 'mcp' object (FastMCP instance)."""
if not MCP_AVAILABLE:
pytest.skip(MCP_SKIP_REASON)
from mcp.server import FastMCP
from framework.mcp.agent_builder_server import mcp
assert mcp is not None
assert isinstance(mcp, FastMCP)
def test_mcp_server_name(self):
"""Test that the MCP server has the expected name."""
if not MCP_AVAILABLE:
pytest.skip(MCP_SKIP_REASON)
from framework.mcp.agent_builder_server import mcp
assert mcp.name == "agent-builder"
class TestMCPPackageExports:
"""Tests for the framework.mcp package exports."""
@@ -87,15 +53,3 @@ class TestMCPPackageExports:
import framework.mcp
assert framework.mcp is not None
def test_agent_builder_server_exported(self):
"""Test that agent_builder_server is exported from framework.mcp."""
if not MCP_AVAILABLE:
pytest.skip(MCP_SKIP_REASON)
from mcp.server import FastMCP
from framework.mcp import agent_builder_server
assert agent_builder_server is not None
assert isinstance(agent_builder_server.mcp, FastMCP)
+9 -60
View File
@@ -101,23 +101,7 @@ def main():
else:
success("all installed")
# Check 3: MCP server module
check("MCP server module")
try:
subprocess.run(
[sys.executable, "-c", "from framework.mcp import agent_builder_server"],
capture_output=True,
text=True,
check=True,
encoding="utf-8",
)
success("loads successfully")
except subprocess.CalledProcessError as e:
error("failed to import")
logger.error(f" Error: {e.stderr}")
all_checks_passed = False
# Check 4: MCP configuration file
# Check 3: MCP configuration file
check("MCP configuration file")
mcp_config = script_dir / ".mcp.json"
if mcp_config.exists():
@@ -125,14 +109,14 @@ def main():
with open(mcp_config, encoding="utf-8") as f:
config = json.load(f)
if "mcpServers" in config and "agent-builder" in config["mcpServers"]:
server_config = config["mcpServers"]["agent-builder"]
if "mcpServers" in config:
success("found and valid")
logger.info(f" Command: {server_config.get('command')}")
logger.info(f" Args: {' '.join(server_config.get('args', []))}")
logger.info(f" CWD: {server_config.get('cwd')}")
for name, server_config in config.get("mcpServers", {}).items():
logger.info(f" Server: {name}")
logger.info(f" Command: {server_config.get('command')}")
logger.info(f" Args: {' '.join(server_config.get('args', []))}")
else:
warning("exists but missing agent-builder config")
warning("exists but missing mcpServers config")
all_checks_passed = False
except json.JSONDecodeError:
error("invalid JSON format")
@@ -140,9 +124,8 @@ def main():
else:
warning("not found (optional)")
logger.info(f" Location would be: {mcp_config}")
logger.info(" Run setup_mcp.py to create it")
# Check 5: Framework modules
# Check 4: Framework modules
check("core framework modules")
modules_to_check = [
"framework.runtime.core",
@@ -170,46 +153,12 @@ def main():
else:
success(f"all {len(modules_to_check)} modules OK")
# Check 6: Test MCP server startup (quick test)
check("MCP server startup")
try:
# Try to import and instantiate the MCP server
result = subprocess.run(
[
sys.executable,
"-c",
"from framework.mcp.agent_builder_server import mcp; print('OK')",
],
capture_output=True,
text=True,
check=True,
timeout=5,
encoding="utf-8",
)
if "OK" in result.stdout:
success("server can start")
else:
warning("unexpected output")
except subprocess.TimeoutExpired:
warning("server startup slow (might be OK)")
except subprocess.CalledProcessError as e:
error("server failed to start")
logger.error(f" Error: {e.stderr}")
all_checks_passed = False
logger.info("")
logger.info("=" * 40)
if all_checks_passed:
logger.info(f"{Colors.GREEN}✓ All checks passed!{Colors.NC}")
logger.info("")
logger.info("Your MCP server is ready to use.")
logger.info("")
logger.info(f"{Colors.BLUE}To start the server:{Colors.NC}")
logger.info(" uv run python -m framework.mcp.agent_builder_server")
logger.info("")
logger.info(f"{Colors.BLUE}To use with Claude Desktop:{Colors.NC}")
logger.info(" Add the configuration from .mcp.json to your")
logger.info(" Claude Desktop MCP settings")
logger.info("Your framework is ready to use.")
else:
logger.info(f"{Colors.RED}✗ Some checks failed{Colors.NC}")
logger.info("")
+11 -11
View File
@@ -13,7 +13,7 @@ Use the Hive agent framework (MCP servers and skills) inside [Antigravity IDE](h
```bash
./scripts/setup-antigravity-mcp.sh
```
3. **Restart Antigravity IDE.** You should see **agent-builder** and **tools** as available MCP servers.
3. **Restart Antigravity IDE.** You should see **coder-tools** and **tools** as available MCP servers.
> **Important:** Always restart/refresh Antigravity IDE after running the setup script or making any changes to MCP configuration. The IDE only loads MCP servers on startup.
@@ -23,7 +23,7 @@ Done. For details, prerequisites, and troubleshooting, read on.
## What you get after setup
- **agent-builder** Create and manage agents (goals, nodes, edges).
- **coder-tools** Create and manage agents (scaffolding via `initialize_agent_package`, file I/O, tool discovery).
- **tools** File operations, web search, and other agent tools.
- **Documentation** Guided docs for building and testing agents.
@@ -68,7 +68,7 @@ The script finds the repo root, writes `~/.gemini/antigravity/mcp_config.json` w
> **Important:** Always restart/refresh Antigravity IDE after running the setup script. MCP servers are only loaded on IDE startup.
The **agent-builder** and **tools** servers should show up after restart.
The **coder-tools** and **tools** servers should show up after restart.
**Using Claude Code instead?** Run:
@@ -82,7 +82,7 @@ That writes `~/.claude/mcp.json` as well.
### Step 3: Use MCP tools + docs
Use the `agent-builder` and `tools` MCP servers in Antigravity, and use docs in `docs/` for workflow guidance.
Use the `coder-tools` and `tools` MCP servers in Antigravity, and use docs in `docs/` for workflow guidance.
---
@@ -90,7 +90,7 @@ Use the `agent-builder` and `tools` MCP servers in Antigravity, and use docs in
```
.agent/
├── mcp_config.json # Template for MCP servers (agent-builder, tools)
├── mcp_config.json # Template for MCP servers (coder-tools, tools)
```
The **setup script** writes your **user** config (`~/.gemini/antigravity/mcp_config.json`) using paths from **this repo**. The file in `.agent/` is the template; Antigravity itself uses the file in your home directory.
@@ -104,7 +104,7 @@ The **setup script** writes your **user** config (`~/.gemini/antigravity/mcp_con
- Run the setup script again from the hive repo root: `./scripts/setup-antigravity-mcp.sh`, then restart Antigravity.
- Make sure Python and deps are installed: from repo root run `./quickstart.sh`.
- Check that the servers can start: from repo root run
`cd core && uv run -m framework.mcp.agent_builder_server` (Ctrl+C to stop), and in another terminal
`cd tools && uv run coder_tools_server.py --stdio` (Ctrl+C to stop), and in another terminal
`cd tools && uv run mcp_server.py --stdio` (Ctrl+C to stop).
If those fail, fix the errors first (e.g. install deps with `uv sync`).
@@ -115,7 +115,7 @@ The **setup script** writes your **user** config (`~/.gemini/antigravity/mcp_con
**MCP tools dont show up in the UI**
- Antigravity may need a restart. Use the files in `docs/` as documentation; the MCP tools (`agent-builder`, `tools`) are the required integration point.
- Antigravity may need a restart. Use the files in `docs/` as documentation; the MCP tools (`coder-tools`, `tools`) are the required integration point.
---
@@ -126,7 +126,7 @@ Paste this into Antigravity to check that MCP is set up. It doesnt use your m
```
Check the Hive + Antigravity integration:
1. MCP: List available MCP servers/tools. Confirm that "agent-builder" and "tools" (or equivalent) are connected. If not, tell the user to run ./scripts/setup-antigravity-mcp.sh from the hive repo root, then restart Antigravity (see docs/antigravity-setup.md).
1. MCP: List available MCP servers/tools. Confirm that "coder-tools" and "tools" (or equivalent) are connected. If not, tell the user to run ./scripts/setup-antigravity-mcp.sh from the hive repo root, then restart Antigravity (see docs/antigravity-setup.md).
2. Docs: Confirm that the project has `docs/` with setup/developer guides for the workflow.
@@ -146,9 +146,9 @@ Save as `~/.gemini/antigravity/mcp_config.json` (Antigravity) or `~/.claude/mcp.
```json
{
"mcpServers": {
"agent-builder": {
"coder-tools": {
"command": "uv",
"args": ["run", "--directory", "/path/to/hive/core", "-m", "framework.mcp.agent_builder_server"],
"args": ["run", "--directory", "/path/to/hive/tools", "coder_tools_server.py", "--stdio"],
"disabled": false
},
"tools": {
@@ -184,7 +184,7 @@ python3 -c "import json; json.load(open('.agent/mcp_config.json')); print('OK: v
```bash
# Terminal 1
cd core && uv run -m framework.mcp.agent_builder_server
cd tools && uv run coder_tools_server.py --stdio
# Terminal 2
cd tools && uv run mcp_server.py --stdio
+8 -8
View File
@@ -20,11 +20,11 @@
| `core/framework/graph/executor.py` | Remove `FunctionNode` import (~L24). Remove `"function"` from `VALID_NODE_TYPES` (~L1473). Remove `node_type == "function"` branch (~L1529-1533). Remove `register_function()` (~L1975-1977). Add migration error for graphs with `node_type="function"`. |
| `core/framework/builder/workflow.py` | Remove `node_type == "function"` validation block (~L258-260). |
### 1.2 Agent Builder MCP server
### 1.2 Builder Package Generator
| File | What to change |
|---|---|
| `core/framework/mcp/agent_builder_server.py` | Remove `"function"` from `node_type` description in `add_node` (~L590) and `update_node` (~L841). Remove `node_type == "function"` simulation branch in `test_node` (~L2356-2357). |
| `core/framework/builder/package_generator.py` | Remove `"function"` from `node_type` description in `add_node` and `update_node`. Remove `node_type == "function"` simulation branch in `test_node`. |
### 1.3 Examples & demos
@@ -56,7 +56,7 @@ Already soft-deprecated with `DeprecationWarning`. No template agent uses them.
|---|---|
| `core/framework/graph/node.py` | Delete `LLMNode` class (~L660-1689, ~1000 lines). Largest single removal. |
| `core/framework/graph/executor.py` | Remove `LLMNode` import. Remove `"llm_tool_use"`/`"llm_generate"` from `VALID_NODE_TYPES`. Remove `DEPRECATED_NODE_TYPES` dict. Remove their branches in `_get_node_implementation` (~L1507-1523). Update `human_input` branch to use `EventLoopNode` instead of `LLMNode`. Add migration error for deprecated types. |
| `core/framework/mcp/agent_builder_server.py` | Remove `llm_tool_use`/`llm_generate` validation warnings and branches (~L668-683, L922-937) |
| `core/framework/builder/package_generator.py` | Remove `llm_tool_use`/`llm_generate` validation warnings and branches |
---
@@ -94,7 +94,7 @@ These tests use `node_type="function"` as convenient scaffolding but actually te
The entire Planner-Worker-Judge pattern has **zero external consumers**. No template agent, example, demo, or runner references it. It is only consumed by:
- Its own internal files (self-referential imports)
- The agent-builder MCP server (exposes tools for it)
- The builder package generator (exposes tools for it)
- Its own dedicated tests
### 5.1 Delete these files entirely
@@ -114,9 +114,9 @@ The entire Planner-Worker-Judge pattern has **zero external consumers**. No temp
`core/framework/graph/__init__.py` — Remove all planner-worker exports: `FlexibleGraphExecutor`, `ExecutorConfig`, `WorkerNode`, `StepExecutionResult`, `HybridJudge`, `create_default_judge`, `CodeSandbox`, `safe_eval`, `safe_exec`, `Plan`, `PlanStep`, `ActionType`, `ActionSpec`, and all related symbols.
### 5.3 Remove MCP tools from agent-builder server
### 5.3 Remove MCP tools from builder package generator
`core/framework/mcp/agent_builder_server.py` — Remove these 7 MCP tools:
`core/framework/builder/package_generator.py` — Remove these 7 MCP tools:
| MCP tool | Description |
|---|---|
@@ -144,7 +144,7 @@ Also remove:
4. **Remove `LLMNode` class + deprecated types** (Part 2)
5. **Delete Planner-Worker subsystem files** (Part 5.1)
6. **Clean up `__init__.py` exports** (Part 5.2)
7. **Remove MCP tools** for plans/evaluation from agent-builder server (Part 5.3)
7. **Remove MCP tools** for plans/evaluation from builder package generator (Part 5.3)
8. **Update examples/demos/docs/skills** (Parts 1.3, 1.4)
9. **Run full test suite** to verify
@@ -157,5 +157,5 @@ Also remove:
3. Load any template agent JSON — no errors
4. Attempt to load a graph with `node_type="function"` — clear `RuntimeError` with migration guidance
5. Attempt to load a graph with `node_type="llm_tool_use"` — clear `RuntimeError` with migration guidance
6. Agent builder MCP: `add_node` with `node_type="function"` — rejected with helpful message
6. Builder package generator: `add_node` with `node_type="function"` — rejected with helpful message
7. Plan/evaluation MCP tools no longer appear in tool list
+4 -4
View File
@@ -116,10 +116,10 @@ MCP (Model Context Protocol) servers are configured in `.mcp.json` at the projec
```json
{
"mcpServers": {
"agent-builder": {
"coder-tools": {
"command": "uv",
"args": ["run", "-m", "framework.mcp.agent_builder_server"],
"cwd": "core"
"args": ["run", "coder_tools_server.py", "--stdio"],
"cwd": "tools"
},
"tools": {
"command": "uv",
@@ -130,7 +130,7 @@ MCP (Model Context Protocol) servers are configured in `.mcp.json` at the projec
}
```
The tools MCP server exposes tools including web search, PDF reading, CSV processing, and file system operations.
The `coder-tools` server provides agent scaffolding via `initialize_agent_package` and related tools. The `tools` MCP server exposes tools including web search, PDF reading, CSV processing, and file system operations.
## Storage
+10 -10
View File
@@ -101,7 +101,7 @@ Get API keys:
./quickstart.sh
```
This sets up agent-builder and tools MCP workflows.
This sets up the MCP tools and workflows for building agents.
### Cursor IDE Support
@@ -117,7 +117,7 @@ MCP tools are also available in Cursor. To enable:
Hive supports [OpenAI Codex CLI](https://github.com/openai/codex) (v0.101.0+).
Configuration files are tracked in git:
- `.codex/config.toml` — MCP server config (`agent-builder`)
- `.codex/config.toml` — MCP server config
To use Codex with Hive:
1. Run `codex` in the repo root
@@ -136,7 +136,7 @@ To enable Opencode integration:
2. Configure MCP servers in `.opencode/mcp.json`
3. Restart Opencode to load the MCP servers
4. Switch to the Hive agent
* **Tools:** Accesses `agent-builder` and standard `tools` via standard MCP protocols over stdio.
* **Tools:** Accesses `coder-tools` and standard `tools` via standard MCP protocols over stdio.
### Verify Setup
@@ -146,7 +146,7 @@ uv run python -c "import framework; print('✓ framework OK')"
uv run python -c "import aden_tools; print('✓ aden_tools OK')"
uv run python -c "import litellm; print('✓ litellm OK')"
# Run an agent (after building one with agent-builder)
# Run an agent (after building one with coder-tools)
PYTHONPATH=exports uv run python -m your_agent_name validate
```
@@ -206,7 +206,7 @@ hive/ # Repository root
│ └── README.md # Tools documentation
├── exports/ # AGENT PACKAGES (user-created, gitignored)
│ └── your_agent_name/ # Created via agent-builder workflow
│ └── your_agent_name/ # Created via coder-tools workflow
├── examples/ # Example agents
│ └── templates/ # Pre-built template agents
@@ -235,7 +235,7 @@ hive/ # Repository root
## Building Agents
### Using Agent Builder Workflow
### Using Coder Tools Workflow
The fastest way to build agents is with the configured MCP workflow:
@@ -244,7 +244,7 @@ The fastest way to build agents is with the configured MCP workflow:
./quickstart.sh
# Build a new agent
Use the agent-builder MCP tools from your IDE agent chat
Use the coder-tools MCP tools from your IDE agent chat (e.g., initialize_agent_package)
```
### Agent Development Workflow
@@ -252,7 +252,7 @@ Use the agent-builder MCP tools from your IDE agent chat
1. **Define Your Goal**
```
Use the agent-builder workflow
Use the coder-tools initialize_agent_package tool
Enter goal: "Build an agent that processes customer support tickets"
```
@@ -555,7 +555,7 @@ uv add <package>
```bash
# Option 1: Use Claude Code skill (recommended)
Use the agent-builder workflow
Use the coder-tools initialize_agent_package tool
# Option 2: Create manually
# Note: exports/ is initially empty (gitignored). Create your agent directory:
@@ -563,7 +563,7 @@ mkdir -p exports/my_new_agent
cd exports/my_new_agent
# Create agent.json, tools.py, README.md (see Agent Package Structure below)
# Option 3: Use the agent builder MCP tools (advanced)
# Option 3: Use the coder-tools MCP tools (advanced)
# See core/MCP_BUILDER_TOOLS_GUIDE.md
```
+8 -8
View File
@@ -165,7 +165,7 @@ Build and run an agent using Claude Code CLI with the agent building skills:
./quickstart.sh
```
This sets up agent-builder and tools MCP workflows.
This sets up the MCP tools and workflows for building agents.
### Cursor IDE Support
@@ -180,7 +180,7 @@ MCP tools are also available in Cursor. To enable:
**Claude Code:**
```
Use the agent-builder workflow
Use the coder-tools initialize_agent_package tool to scaffold a new agent
```
**Codex CLI:**
@@ -361,7 +361,7 @@ hive/
│ └── pyproject.toml
├── exports/ # Agent packages (user-created, gitignored)
│ └── your_agent_name/ # Created via agent-builder workflow
│ └── your_agent_name/ # Created via coder-tools workflow
└── examples/
└── templates/ # Pre-built template agents
@@ -413,10 +413,10 @@ The `.mcp.json` at project root configures MCP servers to run through `uv run` i
```json
{
"mcpServers": {
"agent-builder": {
"coder-tools": {
"command": "uv",
"args": ["run", "-m", "framework.mcp.agent_builder_server"],
"cwd": "core"
"args": ["run", "coder_tools_server.py", "--stdio"],
"cwd": "tools"
},
"tools": {
"command": "uv",
@@ -453,7 +453,7 @@ This design allows agents in `exports/` to be:
### 2. Build Agent (Claude Code)
```
Use the agent-builder workflow
Use the coder-tools initialize_agent_package tool
Enter goal: "Build an agent that processes customer support tickets"
```
@@ -538,7 +538,7 @@ Run the quickstart script in the root directory:
[OpenAI Codex CLI](https://github.com/openai/codex) (v0.101.0+) is supported with project-level config:
- `.codex/config.toml` — MCP server configuration (`agent-builder`)
- `.codex/config.toml` — MCP server configuration
These files are tracked in git and available on clone. To use Codex with Hive:
+4 -4
View File
@@ -47,7 +47,7 @@ This is the recommended way to create your first agent.
# Setup already done via quickstart.sh above
# Start Claude Code and build an agent
Use the agent-builder workflow
Use the coder-tools initialize_agent_package tool
```
Follow the interactive prompts to:
@@ -115,7 +115,7 @@ hive/
│ └── file_system_toolkits/
├── exports/ # Agent Packages (user-generated, not in repo)
│ └── your_agent/ # Your agents created via agent-builder workflow
│ └── your_agent/ # Your agents created via coder-tools workflow
├── examples/
│ └── templates/ # Pre-built template agents
@@ -173,7 +173,7 @@ PYTHONPATH=exports uv run python -m my_agent test --type success
1. **Dashboard**: Run `hive open` to launch the web dashboard, or `hive tui` for the terminal UI
2. **Detailed Setup**: See [environment-setup.md](./environment-setup.md)
3. **Developer Guide**: See [developer-guide.md](./developer-guide.md)
4. **Build Agents**: Use agent-builder workflow in Claude Code
4. **Build Agents**: Use the coder-tools `initialize_agent_package` tool in Claude Code
5. **Custom Tools**: Learn to integrate MCP servers
6. **Join Community**: [Discord](https://discord.com/invite/MXE49hrKDk)
@@ -216,4 +216,4 @@ pip uninstall -y framework tools
- **Documentation**: Check the `/docs` folder
- **Issues**: [github.com/adenhq/hive/issues](https://github.com/adenhq/hive/issues)
- **Discord**: [discord.com/invite/MXE49hrKDk](https://discord.com/invite/MXE49hrKDk)
- **Build Agents**: Use agent-builder workflow to create agents
- **Build Agents**: Use the coder-tools workflow to create agents
+3 -3
View File
@@ -32,7 +32,7 @@ Scan `exports/` for agent packages and `~/.hive/agents/` for runtime data. Retur
### 3-7. Session & Checkpoint Inspection
Ported from `agent_builder_server.py` lines 3484-3856. Pure filesystem reads — JSON + pathlib, zero framework imports.
Ported from the former `agent_builder_server.py`. Pure filesystem reads — JSON + pathlib, zero framework imports.
| Tool | Purpose |
|------|---------|
@@ -40,13 +40,13 @@ Ported from `agent_builder_server.py` lines 3484-3856. Pure filesystem reads —
| `list_agent_checkpoints(agent_name, session_id)` | List checkpoints for debugging |
| `get_agent_checkpoint(agent_name, session_id, checkpoint_id?)` | Load a checkpoint's full state |
**Key difference from agent-builder:** These tools accept `agent_name` (e.g. `"deep_research_agent"`) instead of raw `agent_work_dir` paths. They resolve to `~/.hive/agents/{agent_name}/` internally. Friendlier for the LLM.
**Key difference from the old agent-builder server:** These tools accept `agent_name` (e.g. `"deep_research_agent"`) instead of raw `agent_work_dir` paths. They resolve to `~/.hive/agents/{agent_name}/` internally. Friendlier for the LLM.
### 8. Test Execution
**`run_agent_tests(agent_name, test_types?, fail_fast?)`**
Ported from `agent_builder_server.py` lines 2756-2920. Runs pytest on an agent's test suite, sets PYTHONPATH automatically, parses output into structured results (passed/failed/skipped counts, per-test status, failure details).
Ported from the former `agent_builder_server.py`. Runs pytest on an agent's test suite, sets PYTHONPATH automatically, parses output into structured results (passed/failed/skipped counts, per-test status, failure details).
---
+1 -1
View File
@@ -59,7 +59,7 @@ Full rewrite of account listing and configuration:
---
### Modified: `core/framework/mcp/agent_builder_server.py`
### Modified: `core/framework/builder/package_generator.py`
- `store_credential(name, value, alias="default", ...)` — added `alias` param; now delegates to `LocalCredentialRegistry.save_account()` with auto health check; returns `status` and `identity`
- `list_stored_credentials()` — delegates to `LocalCredentialRegistry.list_accounts()`; returns `credential_id`, `alias`, `status`, `identity`, `last_validated`
+7 -7
View File
@@ -218,7 +218,7 @@ Implement the Goal Creation Session via the Queen Bee and the dynamic Worker Age
- [x] Test case generation
- [x] Test case validation for worker agent
- [x] **Agent Creation Flow**
- [x] Hive Coder reads templates and discovers tools (mcp/agent_builder_server.py)
- [x] Hive Coder reads templates and discovers tools (builder/package_generator.py)
- [x] Generates agent.py, nodes/__init__.py, config.py
- [x] MCP server configuration discovery
- [x] Dynamic tool binding
@@ -330,7 +330,7 @@ Ship essential framework utilities: Node validation, HITL (Human-in-the-loop pau
Port popular tools, and build out the Runtime Log, Audit Trail, Excel, and Email integrations.
- [x] **File Operations (36+ tools)**
- [x] read_file, write_file, edit_file (mcp/agent_builder_server.py)
- [x] read_file, write_file, edit_file (builder/package_generator.py)
- [x] list_directory, search_files
- [x] apply_diff / apply_patch for code modification (tools/file_system_toolkits/)
- [x] data_tools (CSV/Excel parsing)
@@ -435,7 +435,7 @@ Enforce session-local memory isolation to prevent data bleed between concurrent
Implement File I/O support, streaming mode, and allow users to supply custom functions as libraries/nodes.
- [x] **File I/O**
- [x] File read/write operations (mcp/agent_builder_server.py)
- [x] File read/write operations (builder/package_generator.py)
- [x] File system navigation
- [x] Directory listing and search
- [x] **Execution Streaming**
@@ -443,7 +443,7 @@ Implement File I/O support, streaming mode, and allow users to supply custom fun
- [x] Token-by-token output via event bus
- [x] Tool call streaming
- [x] **Custom Tool Integration**
- [x] MCP server discovery (mcp/agent_builder_server.py)
- [x] MCP server discovery (builder/package_generator.py)
- [x] Dynamic tool binding
- [x] Custom tool registration
- [ ] **Streaming Mode Enhancements**
@@ -461,7 +461,7 @@ Implement File I/O support, streaming mode, and allow users to supply custom fun
Add semantic search capabilities and an interactive file system for frontend product integration.
- [x] **File Search**
- [x] search_files tool (mcp/agent_builder_server.py)
- [x] search_files tool (builder/package_generator.py)
- [x] Directory traversal
- [ ] **Semantic Search**
- [ ] Semantic indexing of files
@@ -735,7 +735,7 @@ Ship ~20 ready-to-use templates including GTM Sales, Marketing, Analytics, Train
Build a lightweight local server (e.g., FastAPI or Node) that securely exposes the Hive framework's core Event Bus and Memory Layer to the local browser environment.
- [x] **MCP Server Foundation**
- [x] FastMCP server implementation (mcp/agent_builder_server.py)
- [x] FastMCP server implementation (builder/package_generator.py)
- [x] Agent builder tools exposed
- [x] Port 4001 exposed in Docker
- [x] **Event Bus Architecture**
@@ -802,7 +802,7 @@ Create a UI component to inspect the Shared Memory and Write-Through Conversatio
- [x] **Runtime Logs Tool**
- [x] Inspect agent session logs (tools/runtime_logs_tool/)
- [x] Session state retrieval (mcp/agent_builder_server.py)
- [x] Session state retrieval (builder/package_generator.py)
- [ ] **Memory Inspector UI**
- [ ] Shared Memory visualization
- [ ] Conversation memory view (NodeConversation display)
+1 -1
View File
@@ -22,7 +22,7 @@ template_name/
### Option 1: Build from template (recommended)
Use the agent-builder workflow and select "From a template" to interactively pick a template, customize the goal/nodes/graph, and export a new agent.
Use the `coder-tools` `initialize_agent_package` tool and select "From a template" to interactively pick a template, customize the goal/nodes/graph, and export a new agent.
### Option 2: Manual copy
+2 -3
View File
@@ -721,12 +721,11 @@ $importErrors = 0
$imports = @(
@{ Module = "framework"; Label = "framework"; Required = $true },
@{ Module = "aden_tools"; Label = "aden_tools"; Required = $true },
@{ Module = "litellm"; Label = "litellm"; Required = $false },
@{ Module = "framework.mcp.agent_builder_server"; Label = "MCP server module"; Required = $true }
@{ Module = "litellm"; Label = "litellm"; Required = $false }
)
# Batch check all imports in single process (reduces subprocess spawning overhead)
$modulesToCheck = @("framework", "aden_tools", "litellm", "framework.mcp.agent_builder_server")
$modulesToCheck = @("framework", "aden_tools", "litellm")
try {
$checkOutput = & uv run python scripts/check_requirements.py @modulesToCheck 2>&1 | Out-String
+2 -3
View File
@@ -321,7 +321,7 @@ echo ""
IMPORT_ERRORS=0
# Batch check all imports in single process (reduces subprocess spawning overhead)
CHECK_RESULT=$(uv run python scripts/check_requirements.py framework aden_tools litellm framework.mcp.agent_builder_server 2>/dev/null)
CHECK_RESULT=$(uv run python scripts/check_requirements.py framework aden_tools litellm 2>/dev/null)
CHECK_EXIT=$?
# Parse and display results
@@ -337,8 +337,7 @@ try:
modules = [
('framework', 'framework imports OK', True),
('aden_tools', 'aden_tools imports OK', True),
('litellm', 'litellm imports OK', False),
('framework.mcp.agent_builder_server', 'MCP server module OK', True)
('litellm', 'litellm imports OK', False)
]
import_errors = 0
for mod, label, required in modules:
+1 -1
View File
@@ -49,7 +49,7 @@ Write-Host "Using Python: $PythonCmd" -ForegroundColor Green
Write-Host ""
# Define modules to check
$modules = @("framework", "aden_tools", "litellm", "framework.mcp.agent_builder_server")
$modules = @("framework", "aden_tools", "litellm")
# Benchmark old approach (individual subprocess calls)
Write-Host "Testing OLD approach (individual subprocess calls)..." -ForegroundColor Yellow
+2 -2
View File
@@ -4,7 +4,7 @@
#
# Run from anywhere inside the hive repo. Generates ~/.gemini/antigravity/mcp_config.json
# based on .agent/mcp_config.json template, with absolute paths so the IDE can
# connect to agent-builder and tools MCP servers without manual path editing.
# connect to tools MCP servers without manual path editing.
#
set -e
@@ -56,6 +56,6 @@ fi
echo ""
echo "Next: Restart Antigravity IDE so it loads the MCP config."
echo " Then open this repo; agent-builder and tools should appear."
echo " Then open this repo; tools should appear."
echo ""
echo "For Claude Code, run: $0 --claude"
+44
View File
@@ -0,0 +1,44 @@
"""Quick test script for initialize_agent_package."""
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "core"))
from framework.builder.package_generator import (
BuildSession,
initialize_agent_package,
)
from framework.graph import EdgeCondition, EdgeSpec, Goal, NodeSpec, SuccessCriterion
import framework.builder.package_generator as pg
# Create a minimal build session
session = BuildSession(name="richard_test2")
session.goal = Goal(
id="goal_1",
name="Test Goal",
description="A simple test agent",
success_criteria=[
SuccessCriterion(id="sc_1", description="Completes successfully", metric="llm_judge", target="success")
],
constraints=[],
)
session.nodes = [
NodeSpec(
id="start",
name="Start Node",
description="Entry point",
node_type="event_loop",
input_keys=[],
output_keys=["result"],
system_prompt="You are a helpful assistant.",
),
]
session.edges = []
# Set as active session (in-memory only, no disk persistence)
pg._session = session
# Now call initialize_agent_package
result = initialize_agent_package("richard_test2")
print(result)
+64
View File
@@ -1347,6 +1347,70 @@ def validate_agent_package(agent_name: str) -> str:
)
# ── Meta-agent: Package initialization ─────────────────────────────────────
@mcp.tool()
def initialize_agent_package(agent_name: str, agent_json_path: str) -> str:
"""Generate a full Python agent package from an exported agent.json file.
Reads the agent.json (goal, nodes, edges), validates the graph, and
generates all files needed for a runnable agent in exports/{agent_name}/:
config.py, nodes/__init__.py, agent.py, __init__.py, __main__.py,
mcp_servers.json, tests/conftest.py, agent.json, README.md.
Call this INSTEAD of manually writing package files.
Args:
agent_name: Name for the agent package. Must be snake_case (e.g. 'my_agent').
agent_json_path: Path to the exported agent.json file to import.
Returns:
JSON with files written, validation warnings, and next steps.
"""
resolved = _resolve_path(agent_json_path)
if not os.path.isfile(resolved):
return json.dumps({"success": False, "error": f"File not found: {agent_json_path}"})
env = os.environ.copy()
core_path = os.path.join(PROJECT_ROOT, "core")
exports_path = os.path.join(PROJECT_ROOT, "exports")
fw_agents_path = os.path.join(PROJECT_ROOT, "core", "framework", "agents")
pythonpath = env.get("PYTHONPATH", "")
path_parts = [core_path, exports_path, fw_agents_path, PROJECT_ROOT]
if pythonpath:
path_parts.append(pythonpath)
env["PYTHONPATH"] = os.pathsep.join(path_parts)
try:
proc = subprocess.run(
[
"uv", "run", "python", "-m",
"framework.builder.package_generator",
agent_name,
resolved,
],
capture_output=True,
text=True,
timeout=60,
env=env,
cwd=PROJECT_ROOT,
stdin=subprocess.DEVNULL,
)
if proc.returncode == 0:
return proc.stdout.strip() or json.dumps({"success": True})
else:
return json.dumps({
"success": False,
"error": proc.stderr.strip()[:3000],
"stdout": proc.stdout.strip()[:1000],
})
except subprocess.TimeoutExpired:
return json.dumps({"success": False, "error": "Package generation timed out (60s)"})
except Exception as e:
return json.dumps({"success": False, "error": str(e)})
# ── Main ──────────────────────────────────────────────────────────────────