Merge pull request #53 from bryanadenhq/feat/credential-manager

updates to skills to use credentials and check tools existing
This commit is contained in:
Timothy @aden
2026-01-21 17:40:43 -08:00
committed by GitHub
4 changed files with 119 additions and 13 deletions
+87 -2
View File
@@ -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",
+1
View File
@@ -0,0 +1 @@
../../core/.claude/skills/building-agents
+28 -10
View File
@@ -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."
)
+3 -1
View File
@@ -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,
})