feat: sync full workspace including web modules, docs, and configurations to Gitea
Optimized the root .gitignore to exclude virtual environments, node modules, and temp folders to ensure clean and lightweight version tracking. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
89
axhub-make/skills/third-party/research/SKILL.md
vendored
Normal file
89
axhub-make/skills/third-party/research/SKILL.md
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
---
|
||||
name: research
|
||||
description: "Comprehensive research grounded in web data with explicit citations. Use when you need multi-source synthesis—comparisons, current events, market analysis, detailed reports. "
|
||||
---
|
||||
# Research Skill
|
||||
|
||||
Conduct comprehensive research on any topic with automatic source gathering, analysis, and response generation with citations.
|
||||
|
||||
## Authentication
|
||||
|
||||
The script uses OAuth via the Tavily MCP server. **No manual setup required** - on first run, it will:
|
||||
1. Check for existing tokens in `~/.mcp-auth/`
|
||||
2. If none found, automatically open your browser for OAuth authentication
|
||||
|
||||
> **Note:** You must have an existing Tavily account. The OAuth flow only supports login — account creation is not available through this flow. [Sign up at tavily.com](https://tavily.com) first if you don't have an account.
|
||||
|
||||
### Alternative: API Key
|
||||
|
||||
If you prefer using an API key, get one at https://tavily.com and add to `~/.claude/settings.json`:
|
||||
```json
|
||||
{
|
||||
"env": {
|
||||
"TAVILY_API_KEY": "tvly-your-api-key-here"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
> **Tip**: Research can take 30-120 seconds. Press **Ctrl+B** to run in the background.
|
||||
|
||||
### Using the Script
|
||||
|
||||
```bash
|
||||
./scripts/research.sh '<json>' [output_file]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Basic research
|
||||
./scripts/research.sh '{"input": "quantum computing trends"}'
|
||||
|
||||
# With pro model for comprehensive analysis
|
||||
./scripts/research.sh '{"input": "AI agents comparison", "model": "pro"}'
|
||||
|
||||
# Save to file
|
||||
./scripts/research.sh '{"input": "market analysis for EVs", "model": "pro"}' ./ev-report.md
|
||||
|
||||
# Quick targeted research
|
||||
./scripts/research.sh '{"input": "climate change impacts", "model": "mini"}'
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
|-------|------|---------|-------------|
|
||||
| `input` | string | Required | Research topic or question |
|
||||
| `model` | string | `"mini"` | Model: `mini`, `pro`, `auto` |
|
||||
|
||||
## Model Selection
|
||||
|
||||
**Rule of thumb**: "what does X do?" -> mini. "X vs Y vs Z" or "best way to..." -> pro.
|
||||
|
||||
| Model | Use Case | Speed |
|
||||
|-------|----------|-------|
|
||||
| `mini` | Single topic, targeted research | ~30s |
|
||||
| `pro` | Comprehensive multi-angle analysis | ~60-120s |
|
||||
| `auto` | API chooses based on complexity | Varies |
|
||||
|
||||
## Examples
|
||||
|
||||
### Quick Overview
|
||||
|
||||
```bash
|
||||
./scripts/research.sh '{"input": "What is retrieval augmented generation?", "model": "mini"}'
|
||||
```
|
||||
|
||||
### Technical Comparison
|
||||
|
||||
```bash
|
||||
./scripts/research.sh '{"input": "LangGraph vs CrewAI for multi-agent systems", "model": "pro"}'
|
||||
```
|
||||
|
||||
### Market Research
|
||||
|
||||
```bash
|
||||
./scripts/research.sh '{"input": "Fintech startup landscape 2025", "model": "pro"}' fintech-report.md
|
||||
```
|
||||
182
axhub-make/skills/third-party/research/scripts/research.sh
vendored
Normal file
182
axhub-make/skills/third-party/research/scripts/research.sh
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
#!/bin/bash
|
||||
# Tavily Research API script
|
||||
# Usage: ./research.sh '{"input": "your research query", ...}' [output_file]
|
||||
# Example: ./research.sh '{"input": "quantum computing trends", "model": "pro"}' results.md
|
||||
|
||||
set -e
|
||||
|
||||
# Function to decode JWT payload
|
||||
decode_jwt_payload() {
|
||||
local token="$1"
|
||||
local payload=$(echo "$token" | cut -d'.' -f2)
|
||||
local padded_payload="$payload"
|
||||
case $((${#payload} % 4)) in
|
||||
2) padded_payload="${payload}==" ;;
|
||||
3) padded_payload="${payload}=" ;;
|
||||
esac
|
||||
echo "$padded_payload" | base64 -d 2>/dev/null
|
||||
}
|
||||
|
||||
# Function to check if a JWT is valid for Tavily (not expired and correct issuer)
|
||||
is_valid_tavily_token() {
|
||||
local token="$1"
|
||||
local payload=$(decode_jwt_payload "$token")
|
||||
|
||||
# Check if it's a Tavily token (exact issuer match for security)
|
||||
local iss=$(echo "$payload" | jq -r '.iss // empty' 2>/dev/null)
|
||||
if [ "$iss" != "https://mcp.tavily.com/" ]; then
|
||||
return 1 # Not a valid Tavily token
|
||||
fi
|
||||
|
||||
# Check if expired
|
||||
local exp=$(echo "$payload" | jq -r '.exp // empty' 2>/dev/null)
|
||||
if [ -n "$exp" ] && [ "$exp" != "null" ]; then
|
||||
local current_time=$(date +%s)
|
||||
if [ "$current_time" -ge "$exp" ]; then
|
||||
return 1 # Expired
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0 # Valid Tavily token
|
||||
}
|
||||
|
||||
# Function to find token from MCP auth cache
|
||||
get_mcp_token() {
|
||||
MCP_AUTH_DIR="$HOME/.mcp-auth"
|
||||
if [ -d "$MCP_AUTH_DIR" ]; then
|
||||
# Search recursively for *_tokens.json files
|
||||
while IFS= read -r token_file; do
|
||||
if [ -f "$token_file" ]; then
|
||||
token=$(jq -r '.access_token // empty' "$token_file" 2>/dev/null)
|
||||
if [ -n "$token" ] && [ "$token" != "null" ]; then
|
||||
# Check if valid Tavily token (correct issuer and not expired)
|
||||
if ! is_valid_tavily_token "$token"; then
|
||||
continue # Skip invalid/non-Tavily/expired tokens
|
||||
fi
|
||||
echo "$token"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
done < <(find "$MCP_AUTH_DIR" -name "*_tokens.json" 2>/dev/null)
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Try to load OAuth token from MCP if TAVILY_API_KEY is not set
|
||||
if [ -z "$TAVILY_API_KEY" ]; then
|
||||
token=$(get_mcp_token) || true
|
||||
if [ -n "$token" ]; then
|
||||
export TAVILY_API_KEY="$token"
|
||||
fi
|
||||
fi
|
||||
|
||||
JSON_INPUT="$1"
|
||||
OUTPUT_FILE="$2"
|
||||
|
||||
if [ -z "$JSON_INPUT" ]; then
|
||||
echo "Usage: ./research.sh '<json>' [output_file]"
|
||||
echo ""
|
||||
echo "Required:"
|
||||
echo " input: string - The research topic or question"
|
||||
echo ""
|
||||
echo "Optional:"
|
||||
echo " model: \"mini\" (default), \"pro\", \"auto\""
|
||||
echo " - mini: Targeted, efficient research for narrow questions"
|
||||
echo " - pro: Comprehensive, multi-agent research for complex topics"
|
||||
echo " - auto: Automatically selects based on query complexity"
|
||||
echo ""
|
||||
echo "Arguments:"
|
||||
echo " output_file: optional file to save results"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " ./research.sh '{\"input\": \"AI agent frameworks comparison\", \"model\": \"pro\"}' report.md"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If no token found, run MCP OAuth flow
|
||||
if [ -z "$TAVILY_API_KEY" ]; then
|
||||
set +e
|
||||
echo "No Tavily token found. Initiating OAuth flow..." >&2
|
||||
echo "Please complete authentication in your browser..." >&2
|
||||
npx -y mcp-remote https://mcp.tavily.com/mcp </dev/null >/dev/null 2>&1 &
|
||||
MCP_PID=$!
|
||||
|
||||
TIMEOUT=120
|
||||
ELAPSED=0
|
||||
while [ $ELAPSED -lt $TIMEOUT ]; do
|
||||
sleep 3
|
||||
ELAPSED=$((ELAPSED + 3))
|
||||
|
||||
token=$(get_mcp_token) || true
|
||||
if [ -n "$token" ]; then
|
||||
export TAVILY_API_KEY="$token"
|
||||
echo "Authentication successful!" >&2
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
kill $MCP_PID 2>/dev/null || true
|
||||
wait $MCP_PID 2>/dev/null || true
|
||||
set -e
|
||||
fi
|
||||
|
||||
if [ -z "$TAVILY_API_KEY" ]; then
|
||||
echo "Error: Failed to obtain Tavily API token"
|
||||
echo "Note: The OAuth flow requires an existing Tavily account — account creation is not supported through this flow."
|
||||
echo "Please sign up at https://tavily.com first, then retry, or set TAVILY_API_KEY manually."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate JSON
|
||||
if ! echo "$JSON_INPUT" | jq empty 2>/dev/null; then
|
||||
echo "Error: Invalid JSON input"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for required input field
|
||||
if ! echo "$JSON_INPUT" | jq -e '.input' >/dev/null 2>&1; then
|
||||
echo "Error: 'input' field is required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
INPUT=$(echo "$JSON_INPUT" | jq -r '.input')
|
||||
MODEL=$(echo "$JSON_INPUT" | jq -r '.model // "mini"')
|
||||
|
||||
echo "Researching: $INPUT (model: $MODEL)"
|
||||
echo "This may take 30-120 seconds..."
|
||||
|
||||
# Build MCP JSON-RPC request
|
||||
MCP_REQUEST=$(jq -n --argjson args "$JSON_INPUT" '{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "tavily_research",
|
||||
"arguments": $args
|
||||
}
|
||||
}')
|
||||
|
||||
# Call Tavily MCP server via HTTPS (SSE response)
|
||||
RESPONSE=$(curl -s --request POST \
|
||||
--url "https://mcp.tavily.com/mcp" \
|
||||
--header "Authorization: Bearer $TAVILY_API_KEY" \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Accept: application/json, text/event-stream' \
|
||||
--header 'x-client-source: claude-code-skill' \
|
||||
--data "$MCP_REQUEST")
|
||||
|
||||
# Parse SSE response and extract the JSON result
|
||||
JSON_DATA=$(echo "$RESPONSE" | grep '^data:' | sed 's/^data://' | head -1)
|
||||
|
||||
if [ -z "$JSON_DATA" ]; then
|
||||
RESULT="$RESPONSE"
|
||||
else
|
||||
RESULT=$(echo "$JSON_DATA" | jq '.result.structuredContent // .result.content[0].text // .error // .' 2>/dev/null || echo "$JSON_DATA")
|
||||
fi
|
||||
|
||||
if [ -n "$OUTPUT_FILE" ]; then
|
||||
echo "$RESULT" > "$OUTPUT_FILE"
|
||||
echo "Results saved to: $OUTPUT_FILE"
|
||||
else
|
||||
echo "$RESULT"
|
||||
fi
|
||||
Reference in New Issue
Block a user