Skip to content

Commit eefa957

Browse files
committed
Agents SDK
1 parent b0977a1 commit eefa957

File tree

10 files changed

+2977
-142
lines changed

10 files changed

+2977
-142
lines changed

packages/agents/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
## 0.1.0-keycardai-agents (2025-12-03)
2+
3+
### Features
4+
5+
- feat(keycardai-agents): initial release
6+
- CrewAI integration with secure MCP tool access
7+
- No token passing - agents never receive raw API tokens
8+
- Fresh token fetched per API call through Keycard
9+
- Authentication awareness with auth request tools
10+
- Automatic tool conversion from MCP to CrewAI BaseTool format
11+
- Configurable auth handlers for custom authentication flows
12+
- System prompt generation with auth context

packages/agents/README.md

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
# KeycardAI Agents SDK
2+
3+
Keycard integration for AI agent frameworks (CrewAI) with secure MCP tool access.
4+
5+
## Overview
6+
7+
The `keycardai-agents` package provides clean integrations between Keycard's secure MCP client and popular agent frameworks. It follows these security principles:
8+
9+
1. **No Token Passing**: Agents never receive raw API tokens
10+
2. **Fresh Tokens**: Each tool call fetches a fresh token through Keycard
11+
3. **User Attribution**: All API calls are logged with user identity
12+
4. **Auth Awareness**: Agents can request authentication when needed
13+
14+
## Installation
15+
16+
```bash
17+
# For CrewAI support
18+
pip install keycardai-agents[crewai]
19+
20+
# Or install all extras
21+
pip install keycardai-agents[all]
22+
```
23+
24+
## Quick Start - CrewAI
25+
26+
```python
27+
import asyncio
28+
from crewai import Agent, Crew, Task
29+
from keycardai.agents.crewai_agents import create_client
30+
from keycardai.mcp.client import Client as MCPClient
31+
32+
async def main():
33+
# 1. Configure MCP client to connect to your MCP servers
34+
mcp_config = {
35+
"mcpServers": {
36+
"github": {
37+
"command": "uvx",
38+
"args": ["keycardai-mcp-fastmcp", "--project-dir", "~/your-mcp-server"],
39+
}
40+
}
41+
}
42+
mcp_client = MCPClient(mcp_config)
43+
44+
# 2. Create Keycard CrewAI adapter
45+
async with create_client(mcp_client) as client:
46+
# 3. Get Keycard-secured tools (NO TOKENS!)
47+
tools = await client.get_tools()
48+
auth_tools = await client.get_auth_tools()
49+
50+
# 4. Create agents with secured tools
51+
agent = Agent(
52+
role="GitHub Expert",
53+
goal="Analyze pull requests",
54+
backstory=client.get_system_prompt("You are a code review expert"),
55+
tools=tools + auth_tools, # Keycard-secured tools
56+
)
57+
58+
# 5. Define and run tasks
59+
task = Task(
60+
description="Analyze PR #123 from repo owner/repo",
61+
expected_output="Detailed PR analysis",
62+
agent=agent,
63+
)
64+
65+
crew = Crew(agents=[agent], tasks=[task])
66+
result = crew.kickoff()
67+
print(result)
68+
69+
asyncio.run(main())
70+
```
71+
72+
## Architecture
73+
74+
### The Wrong Way (What NOT to Do)
75+
76+
```python
77+
# ❌ WRONG: Passing tokens directly to agents
78+
github_token = get_token_from_keycard() # Token fetched once
79+
agent = Agent(
80+
role="GitHub Expert",
81+
tools=create_github_tools(github_token), # Token embedded in tools
82+
)
83+
# Problems:
84+
# - Agent has unfettered access to token
85+
# - Token can expire during execution
86+
# - No per-call logging or authorization
87+
# - Keycard is just an "auth orchestrator"
88+
```
89+
90+
### The Right Way (Keycard Integration)
91+
92+
```python
93+
# ✅ CORRECT: Tools call through MCP client
94+
async with create_client(mcp_client) as client:
95+
tools = await client.get_tools() # Tools with MCP client reference
96+
agent = Agent(role="GitHub Expert", tools=tools)
97+
# Each tool call:
98+
# 1. Agent invokes tool
99+
# 2. Tool calls mcp_client.call_tool()
100+
# 3. MCP client calls MCP server
101+
# 4. MCP server requests fresh token from Keycard
102+
# 5. API call is made with fresh token
103+
# 6. Call is logged with user attribution
104+
```
105+
106+
## Key Components
107+
108+
### CrewAIClient
109+
110+
The main adapter class that wraps an MCP client and provides CrewAI-compatible tools.
111+
112+
```python
113+
from keycardai.agents.crewai_agents import CrewAIClient, create_client
114+
115+
async with create_client(mcp_client) as client:
116+
# Get tools from authenticated MCP servers
117+
tools = await client.get_tools() # List[BaseTool]
118+
119+
# Get authentication request tools
120+
auth_tools = await client.get_auth_tools() # List[BaseTool]
121+
122+
# Get system prompt with auth context
123+
prompt = client.get_system_prompt("You are helpful") # str
124+
```
125+
126+
### Tool Conversion
127+
128+
MCP tools are automatically converted to CrewAI `BaseTool` instances:
129+
130+
- **Async Execution**: Tools use `async_run()` for async MCP calls
131+
- **Schema Conversion**: JSON schemas converted to Pydantic models
132+
- **Error Handling**: Errors are caught and returned as strings
133+
- **Result Formatting**: MCP results formatted for agent consumption
134+
135+
### Authentication Flow
136+
137+
When an MCP server requires authentication:
138+
139+
1. **Auth Detection**: Client detects pending auth challenges on connect
140+
2. **Auth Tool**: `request_authentication` tool is added to agent toolset
141+
3. **Agent Request**: Agent can call the tool when user needs a service
142+
4. **Auth Handler**: Configurable handler sends auth link to user
143+
5. **Token Refresh**: After auth, tools automatically use new tokens
144+
145+
```python
146+
# Custom auth handler example
147+
from keycardai.agents.crewai_agents import AuthToolHandler
148+
149+
class SlackAuthHandler(AuthToolHandler):
150+
async def handle_auth_request(self, service, reason, challenge):
151+
# Send auth link to Slack
152+
await slack_client.post_message(
153+
channel=channel_id,
154+
text=f"Please authorize {service}: {challenge['auth_url']}"
155+
)
156+
return f"Authorization link sent to Slack channel"
157+
158+
async with create_client(mcp_client, auth_tool_handler=SlackAuthHandler()) as client:
159+
# Auth requests will be sent to Slack
160+
tools = await client.get_tools()
161+
```
162+
163+
## Advanced Usage
164+
165+
### Multi-Agent Crews
166+
167+
```python
168+
async with create_client(mcp_client) as client:
169+
tools = await client.get_tools()
170+
171+
# Agent 1: Data fetcher
172+
fetcher = Agent(
173+
role="Data Fetcher",
174+
goal="Fetch information from APIs",
175+
tools=tools,
176+
)
177+
178+
# Agent 2: Analyzer
179+
analyzer = Agent(
180+
role="Data Analyzer",
181+
goal="Analyze fetched data",
182+
tools=tools, # Same tools, but each call is independently authorized
183+
)
184+
185+
# Tasks with context passing
186+
fetch_task = Task(
187+
description="Fetch PR #123",
188+
agent=fetcher,
189+
)
190+
191+
analyze_task = Task(
192+
description="Analyze the PR data",
193+
agent=analyzer,
194+
context=[fetch_task], # Uses fetcher's output
195+
)
196+
197+
crew = Crew(
198+
agents=[fetcher, analyzer],
199+
tasks=[fetch_task, analyze_task],
200+
)
201+
result = crew.kickoff()
202+
```
203+
204+
### Custom System Prompts
205+
206+
```python
207+
async with create_client(mcp_client) as client:
208+
# Default auth-aware prompt
209+
default_prompt = client.get_system_prompt("You are helpful")
210+
211+
# Custom auth prompt
212+
client.auth_prompt = """
213+
**IMPORTANT**: Some services need authorization.
214+
Use the request_authentication tool with clear reasoning.
215+
"""
216+
custom_prompt = client.get_system_prompt("You are helpful")
217+
```
218+
219+
### Tool Caching
220+
221+
Tools are cached after first load for performance:
222+
223+
```python
224+
async with create_client(mcp_client) as client:
225+
tools1 = await client.get_tools() # Fetches from MCP servers
226+
tools2 = await client.get_tools() # Returns cached tools
227+
228+
# To force refresh, reconnect:
229+
await mcp_client.disconnect()
230+
await mcp_client.connect()
231+
tools3 = await client.get_tools() # Fresh fetch
232+
```
233+
234+
## Examples
235+
236+
Complete working examples with multi-agent crews, Keycard-secured API access, no token passing, and auth-aware agents will be added in future releases.
237+
238+
## Comparison with Other Integrations
239+
240+
### LangChain Integration
241+
242+
For LangChain, use the existing `keycardai-mcp` integration:
243+
244+
```python
245+
from keycardai.mcp.client.integrations import langchain_agents
246+
247+
async with langchain_agents.create_client(mcp_client) as client:
248+
tools = await client.get_tools() # LangChain StructuredTool objects
249+
# Use with LangChain agents
250+
```
251+
252+
### OpenAI Agents Integration
253+
254+
For OpenAI Agents SDK, use the `keycardai-mcp` integration:
255+
256+
```python
257+
from keycardai.mcp.client.integrations import openai_agents
258+
259+
async with openai_agents.create_client(mcp_client) as client:
260+
tools = await client.get_tools() # OpenAI tool schemas
261+
# Use with OpenAI agents
262+
```
263+
264+
## Framework Comparison
265+
266+
| Framework | Package | Tool Format | Agent Type |
267+
|-----------|---------|-------------|------------|
268+
| CrewAI | `keycardai-agents[crewai]` | `BaseTool` | Role-based |
269+
| LangChain | `keycardai-mcp` | `StructuredTool` | Conversational |
270+
| OpenAI Agents | `keycardai-mcp` | OpenAI schema | Function calling |
271+
272+
## Development
273+
274+
```bash
275+
# Install in development mode
276+
cd python-sdk/packages/agents
277+
pip install -e ".[test]"
278+
279+
# Run tests
280+
pytest
281+
282+
# Run type checking
283+
mypy src/keycardai/agents
284+
285+
# Run linting
286+
ruff check src/keycardai/agents
287+
```
288+
289+
## License
290+
291+
MIT
292+
293+
## Links
294+
295+
- [GitHub Repository](https://github.com/keycardai/python-sdk)
296+
- [Documentation](https://docs.keycardai.com)
297+
- [Keycard Platform](https://keycard.ai)

0 commit comments

Comments
 (0)