Merge pull request #53 from bryanadenhq/feat/credential-manager
updates to skills to use credentials and check tools existing
This commit is contained in:
@@ -46,6 +46,82 @@ exports/my_agent/
|
||||
- Pause nodes stop execution, wait for user input
|
||||
- Resume entry points continue from pause with user's response
|
||||
|
||||
## Tool Discovery & Validation
|
||||
|
||||
**CRITICAL:** Before adding a node with tools, you MUST verify the tools exist.
|
||||
|
||||
Tools are provided by MCP servers. Never assume a tool exists - always discover dynamically.
|
||||
|
||||
### Step 1: Register MCP Server (if not already done)
|
||||
|
||||
```python
|
||||
mcp__agent-builder__add_mcp_server(
|
||||
name="aden-tools",
|
||||
transport="stdio",
|
||||
command="python",
|
||||
args='["mcp_server.py", "--stdio"]',
|
||||
cwd="../aden-tools"
|
||||
)
|
||||
```
|
||||
|
||||
### Step 2: Discover Available Tools
|
||||
|
||||
```python
|
||||
# List all tools from all registered servers
|
||||
mcp__agent-builder__list_mcp_tools()
|
||||
|
||||
# Or list tools from a specific server
|
||||
mcp__agent-builder__list_mcp_tools(server_name="aden-tools")
|
||||
```
|
||||
|
||||
This returns available tools with their descriptions and parameters:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"tools_by_server": {
|
||||
"aden-tools": [
|
||||
{"name": "web_search", "description": "Search the web...", "parameters": ["query"]},
|
||||
{"name": "web_scrape", "description": "Scrape a URL...", "parameters": ["url"]}
|
||||
]
|
||||
},
|
||||
"total_tools": 14
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Validate Before Adding Nodes
|
||||
|
||||
Before writing a node with `tools=[...]`:
|
||||
1. Call `list_mcp_tools()` to get available tools
|
||||
2. Check each tool in your node exists in the response
|
||||
3. If a tool doesn't exist:
|
||||
- **DO NOT proceed** with the node
|
||||
- Inform the user: "The tool 'X' is not available. Available tools are: ..."
|
||||
- Ask if they want to use an alternative or proceed without the tool
|
||||
|
||||
### Tool Validation Anti-Patterns
|
||||
|
||||
❌ **Never assume a tool exists** - always call `list_mcp_tools()` first
|
||||
❌ **Never write a node with unverified tools** - validate before writing
|
||||
❌ **Never silently drop tools** - if a tool doesn't exist, inform the user
|
||||
❌ **Never guess tool names** - use exact names from discovery response
|
||||
|
||||
### Example Validation Flow
|
||||
|
||||
```python
|
||||
# 1. User requests: "Add a node that searches the web"
|
||||
# 2. Discover available tools
|
||||
tools_response = mcp__agent-builder__list_mcp_tools()
|
||||
|
||||
# 3. Check if web_search exists
|
||||
available = [t["name"] for tools in tools_response["tools_by_server"].values() for t in tools]
|
||||
if "web_search" not in available:
|
||||
# Inform user and ask how to proceed
|
||||
print("❌ 'web_search' not available. Available tools:", available)
|
||||
else:
|
||||
# Proceed with node creation
|
||||
# ...
|
||||
```
|
||||
|
||||
## Workflow: Incremental File Construction
|
||||
|
||||
```
|
||||
@@ -128,6 +204,7 @@ from framework.graph.executor import GraphExecutor
|
||||
from framework.runtime import Runtime
|
||||
from framework.llm.anthropic import AnthropicProvider
|
||||
from framework.runner.tool_registry import ToolRegistry
|
||||
from aden_tools.credentials import CredentialManager
|
||||
|
||||
# Goal will be added when defined
|
||||
# Nodes will be imported from .nodes
|
||||
@@ -243,6 +320,11 @@ Open exports/technical_research_agent/agent.py to see the goal!
|
||||
|
||||
### Step 3: Add Nodes (Incremental)
|
||||
|
||||
**⚠️ IMPORTANT:** Before adding any node with tools, you MUST:
|
||||
1. Call `mcp__agent-builder__list_mcp_tools()` to discover available tools
|
||||
2. Verify each tool exists in the response
|
||||
3. If a tool doesn't exist, inform the user and ask how to proceed
|
||||
|
||||
For each node, **write immediately after approval**:
|
||||
|
||||
```python
|
||||
@@ -417,8 +499,11 @@ class {agent_class_name}:
|
||||
tool_registry = ToolRegistry()
|
||||
|
||||
llm = None
|
||||
if not mock_mode and os.environ.get("ANTHROPIC_API_KEY"):
|
||||
llm = AnthropicProvider(model=self.config.model)
|
||||
if not mock_mode:
|
||||
creds = CredentialManager()
|
||||
if creds.is_available("anthropic"):
|
||||
api_key = creds.get("anthropic")
|
||||
llm = AnthropicProvider(api_key=api_key, model=self.config.model)
|
||||
|
||||
graph = GraphSpec(
|
||||
id="{agent_name}-graph",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
../../core/.claude/skills/building-agents
|
||||
@@ -88,10 +88,11 @@ When generating tests, **ALWAYS include API key checks**:
|
||||
```python
|
||||
import os
|
||||
import pytest
|
||||
from aden_tools.credentials import CredentialManager
|
||||
|
||||
# At the top of every test file
|
||||
pytestmark = pytest.mark.skipif(
|
||||
not os.environ.get("ANTHROPIC_API_KEY") and not os.environ.get("MOCK_MODE"),
|
||||
not CredentialManager().is_available("anthropic") and not os.environ.get("MOCK_MODE"),
|
||||
reason="API key required for real testing. Set ANTHROPIC_API_KEY or use MOCK_MODE=1 for structure validation only."
|
||||
)
|
||||
|
||||
@@ -99,7 +100,8 @@ pytestmark = pytest.mark.skipif(
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def check_api_key():
|
||||
"""Ensure API key is set for real testing."""
|
||||
if not os.environ.get("ANTHROPIC_API_KEY"):
|
||||
creds = CredentialManager()
|
||||
if not creds.is_available("anthropic"):
|
||||
if os.environ.get("MOCK_MODE"):
|
||||
print("\n⚠️ Running in MOCK MODE - structure validation only")
|
||||
print(" This does NOT test LLM behavior or agent quality")
|
||||
@@ -121,8 +123,11 @@ def check_api_key():
|
||||
When the user asks to test an agent, **ALWAYS check for the API key first**:
|
||||
|
||||
```python
|
||||
from aden_tools.credentials import CredentialManager
|
||||
|
||||
# Before running any tests
|
||||
if not os.environ.get("ANTHROPIC_API_KEY"):
|
||||
creds = CredentialManager()
|
||||
if not creds.is_available("anthropic"):
|
||||
print("⚠️ No ANTHROPIC_API_KEY found!")
|
||||
print()
|
||||
print("Testing requires a real API key to validate agent behavior.")
|
||||
@@ -215,11 +220,12 @@ REQUIRES: ANTHROPIC_API_KEY for real testing.
|
||||
import os
|
||||
import pytest
|
||||
from exports.{agent_name} import default_agent
|
||||
from aden_tools.credentials import CredentialManager
|
||||
|
||||
|
||||
# Enforce API key for real testing
|
||||
pytestmark = pytest.mark.skipif(
|
||||
not os.environ.get("ANTHROPIC_API_KEY") and not os.environ.get("MOCK_MODE"),
|
||||
not CredentialManager().is_available("anthropic") and not os.environ.get("MOCK_MODE"),
|
||||
reason="API key required. Set ANTHROPIC_API_KEY or use MOCK_MODE=1."
|
||||
)
|
||||
|
||||
@@ -318,11 +324,12 @@ REQUIRES: ANTHROPIC_API_KEY for real testing - mock mode cannot validate success
|
||||
import os
|
||||
import pytest
|
||||
from exports.{agent_name} import default_agent
|
||||
from aden_tools.credentials import CredentialManager
|
||||
|
||||
|
||||
# Enforce API key for real testing
|
||||
pytestmark = pytest.mark.skipif(
|
||||
not os.environ.get("ANTHROPIC_API_KEY") and not os.environ.get("MOCK_MODE"),
|
||||
not CredentialManager().is_available("anthropic") and not os.environ.get("MOCK_MODE"),
|
||||
reason="API key required. Set ANTHROPIC_API_KEY or use MOCK_MODE=1."
|
||||
)
|
||||
|
||||
@@ -381,11 +388,12 @@ conftest_content = '''"""Shared test fixtures for {agent_name} tests."""
|
||||
import os
|
||||
import pytest
|
||||
import asyncio
|
||||
from aden_tools.credentials import CredentialManager
|
||||
|
||||
|
||||
# Enforce API key requirement for real testing
|
||||
pytestmark = pytest.mark.skipif(
|
||||
not os.environ.get("ANTHROPIC_API_KEY") and not os.environ.get("MOCK_MODE"),
|
||||
not CredentialManager().is_available("anthropic") and not os.environ.get("MOCK_MODE"),
|
||||
reason="API key required for real testing. Set ANTHROPIC_API_KEY or use MOCK_MODE=1 for structure validation only."
|
||||
)
|
||||
|
||||
@@ -393,7 +401,8 @@ pytestmark = pytest.mark.skipif(
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def check_api_key():
|
||||
"""Ensure API key is set for real testing."""
|
||||
if not os.environ.get("ANTHROPIC_API_KEY"):
|
||||
creds = CredentialManager()
|
||||
if not creds.is_available("anthropic"):
|
||||
if os.environ.get("MOCK_MODE"):
|
||||
print("\\n⚠️ Running in MOCK MODE - structure validation only")
|
||||
print(" This does NOT test LLM behavior or agent quality")
|
||||
@@ -410,6 +419,12 @@ def check_api_key():
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def credentials():
|
||||
"""Provide CredentialManager instance to tests (with hot-reload support)."""
|
||||
return CredentialManager()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_inputs():
|
||||
"""Sample inputs for testing."""
|
||||
@@ -752,11 +767,12 @@ Requires ANTHROPIC_API_KEY for real testing.
|
||||
import os
|
||||
import pytest
|
||||
from exports.{agent_name} import default_agent
|
||||
from aden_tools.credentials import CredentialManager
|
||||
|
||||
|
||||
# Enforce API key for real testing
|
||||
pytestmark = pytest.mark.skipif(
|
||||
not os.environ.get("ANTHROPIC_API_KEY") and not os.environ.get("MOCK_MODE"),
|
||||
not CredentialManager().is_available("anthropic") and not os.environ.get("MOCK_MODE"),
|
||||
reason="API key required. Set ANTHROPIC_API_KEY or use MOCK_MODE=1."
|
||||
)
|
||||
|
||||
@@ -784,11 +800,12 @@ Requires ANTHROPIC_API_KEY for real testing - mock mode cannot validate success
|
||||
import os
|
||||
import pytest
|
||||
from exports.{agent_name} import default_agent
|
||||
from aden_tools.credentials import CredentialManager
|
||||
|
||||
|
||||
# Enforce API key for real testing
|
||||
pytestmark = pytest.mark.skipif(
|
||||
not os.environ.get("ANTHROPIC_API_KEY") and not os.environ.get("MOCK_MODE"),
|
||||
not CredentialManager().is_available("anthropic") and not os.environ.get("MOCK_MODE"),
|
||||
reason="API key required. Set ANTHROPIC_API_KEY or use MOCK_MODE=1."
|
||||
)
|
||||
|
||||
@@ -818,11 +835,12 @@ Requires ANTHROPIC_API_KEY for real testing.
|
||||
import os
|
||||
import pytest
|
||||
from exports.{agent_name} import default_agent
|
||||
from aden_tools.credentials import CredentialManager
|
||||
|
||||
|
||||
# Enforce API key for real testing
|
||||
pytestmark = pytest.mark.skipif(
|
||||
not os.environ.get("ANTHROPIC_API_KEY") and not os.environ.get("MOCK_MODE"),
|
||||
not CredentialManager().is_available("anthropic") and not os.environ.get("MOCK_MODE"),
|
||||
reason="API key required. Set ANTHROPIC_API_KEY or use MOCK_MODE=1."
|
||||
)
|
||||
|
||||
|
||||
@@ -144,10 +144,12 @@ class AnthropicProvider(LLMProvider):
|
||||
tool_results = []
|
||||
for tool_use in tool_uses:
|
||||
result = tool_executor(tool_use)
|
||||
# Ensure content is never empty (Anthropic API requires non-empty content)
|
||||
content = result.content if result.content else "(empty result)"
|
||||
tool_results.append({
|
||||
"type": "tool_result",
|
||||
"tool_use_id": result.tool_use_id,
|
||||
"content": result.content,
|
||||
"content": content,
|
||||
"is_error": result.is_error,
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user