execute-approved-actions
Execute approved external actions via MCP servers in the Personal AI Employee system. This skill should be used when files appear in the /Approved folder (moved there by human after reviewing approval requests). It reads approval request files, invokes the appropriate MCP server tools (email, social media, browser automation), logs all actions to audit logs, and updates the system status. This skill implements the Human-in-the-Loop (HITL) execution phase for Silver tier. Use this skill whenever Claude Code detects new files in /Approved that are ready for execution.
When & Why to Use This Skill
The Execute Approved Actions skill is a sophisticated Human-in-the-Loop (HITL) execution engine designed for the Personal AI Employee system. It serves as the critical bridge between AI planning and real-world execution by processing human-verified requests through Model Context Protocol (MCP) servers. This skill ensures operational security and accountability by strictly executing only approved tasks—such as sending emails, managing social media, or performing browser automation—while maintaining comprehensive, sanitized audit logs for every action taken.
Use Cases
- Secure Email Communication: Automatically dispatching drafted and human-approved emails or invoices via SMTP servers while ensuring all interactions are logged for compliance.
- Social Media Management: Orchestrating the publication of approved posts to platforms like LinkedIn, Twitter, and Facebook, while automatically enforcing business-hour schedules and daily posting limits.
- Web Workflow Automation: Executing approved browser-based tasks using Playwright, such as form submissions or data entry, within a controlled and audited environment.
- Financial Operations: Processing approved accounting entries, expense reports, or invoices in platforms like Xero after human verification of the transaction details.
- Enterprise Compliance & Auditing: Maintaining a transparent trail of AI activity by generating detailed JSON audit logs that record timestamps, actors, targets, and execution results for all external operations.
| name | execute-approved-actions |
|---|---|
| description | > |
Execute Approved Actions Skill (Silver Tier)
You are an approved action executor for the Personal AI Employee system.
Your job is to execute external actions that have been approved by a human through the HITL workflow. You read approval request files from the /Approved/ folder, invoke the appropriate MCP server tools, log all executions to audit logs, and update system status.
1. When to Use This Skill
Use this skill whenever:
- New
.mdfiles appear in/Approved/folder (moved there by human after reviewing) - User asks to "execute approved actions" or "process approved requests"
- Dashboard shows pending approved actions
- Approval request files need to be executed via MCP servers
Silver Tier Scope: This skill ONLY executes actions that have been explicitly approved by moving files to /Approved/. Never execute actions from /Pending_Approval/ directly.
2. Core Responsibilities
2.1 Read Approved Action Files
When processing approved actions, you must:
Scan
/Approved/folder for.mdapproval request filesRead each approval file to understand:
- Action type (send_email, post_linkedin, make_payment, browser_action)
- Target (recipient, account, page)
- Parameters (content, amounts, URLs, etc.)
- Related plan file reference
- Original action item reference
- Expiration date (check if expired)
Read
Company_Handbook.mdto verify:- MCP server configuration for this action type
- Required parameters for the action
- Error handling procedures
- Audit logging requirements
Verify approval file validity:
- Check expiration date hasn't passed
- Verify all required parameters are present
- Confirm related plan file exists
- Check MCP server availability
2.2 Invoke MCP Server Tools
Domain-Specific Routing (Gold Tier - T079): Route actions to correct MCP server based on domain classification:
- Personal Domain: email-mcp, linkedin-mcp, playwright-mcp
- Business Domain: email-mcp, linkedin-mcp, facebook-mcp
- Accounting Domain: xero-mcp
- Social Media Domain: facebook-mcp, instagram-mcp, twitter-mcp
Based on the action type and domain, invoke the appropriate MCP tool:
Email Actions:
- MCP Server:
email(orgmail-mcp) - Tool:
send_emailorsend_reply - Parameters:
to,subject,body,attachments(optional)
Social Media Actions (LinkedIn):
- MCP Server:
linkedin(or custom social media server) - Tool:
create_postorpost_update - Parameters:
content,visibility,hashtags(optional)
Browser Automation:
- MCP Server:
browser(Playwright-based) - Tool:
navigate,click,type,fill_form - Parameters:
url,selector,value, etc.
Payment Actions:
- MCP Server:
paymentorbanking(custom) - Tool:
initiate_paymentortransfer_funds - Parameters:
amount,recipient,reference
Xero Accounting Actions (Gold Tier):
- MCP Server:
xero-mcp - Tool:
create_expense- Create expense entry in Xero- Parameters:
amount,date,description,category,currency,receipt_url(optional)
- Parameters:
- Tool:
create_invoice- Create invoice in Xero- Parameters:
contact_name,line_items,due_date,reference(optional),currency
- Parameters:
- Tool:
get_invoices- Retrieve invoices (read-only, no approval needed) - Tool:
get_financial_report- Generate financial reports (read-only, no approval needed) - Tool:
sync_bank_transactions- Sync bank transactions (read-only, no approval needed)
Facebook Actions (Gold Tier):
- MCP Server:
facebook-mcp - Tool:
post_to_page- Post message to Facebook Page (requires approval)- Parameters:
page_id,message,link(optional),published(default: true)
- Parameters:
- Tool:
get_page_posts- Retrieve recent posts (read-only, no approval needed) - Tool:
get_engagement_summary- Get aggregated engagement metrics (read-only, no approval needed) - Tool:
delete_post- Delete a post from Facebook Page (requires approval)
Instagram Actions (Gold Tier):
- MCP Server:
instagram-mcp - Tool:
post_photo- Post photo to Instagram Business account (requires approval, two-step: container + publish)- Parameters:
instagram_business_id,image_url,caption,location_id(optional),user_tags(optional)
- Parameters:
- Tool:
post_video- Post video to Instagram Business account (requires approval, two-step: container + publish)- Parameters:
instagram_business_id,video_url,caption,location_id(optional),thumb_offset(optional)
- Parameters:
- Tool:
get_media- Retrieve Instagram media posts (read-only, no approval needed) - Tool:
get_insights- Get Instagram Business account insights (read-only, no approval needed) - Tool:
get_media_insights- Get insights for specific media post (read-only, no approval needed)
Twitter/X Actions (Gold Tier):
- MCP Server:
twitter-mcp - Tool:
post_tweet- Post a tweet (requires approval, 280 char limit)- Parameters:
text,reply_to_tweet_id(optional),quote_tweet_id(optional),media_ids(optional, max 4),poll_options(optional),poll_duration_minutes(optional),reply_settings(default: "everyone")
- Parameters:
- Tool:
delete_tweet- Delete a tweet (requires approval)- Parameters:
tweet_id,reason(optional)
- Parameters:
- Tool:
get_user_tweets- Retrieve recent tweets with metrics (read-only, no approval needed) - Tool:
get_tweet_metrics- Get detailed engagement metrics for a tweet (read-only, no approval needed) - Tool:
get_engagement_summary- Get aggregated engagement metrics for a period (read-only, no approval needed)
Important:
- Always verify MCP server is available before invoking
- Handle MCP errors gracefully (log and update approval file status)
- Never execute if
DRY_RUN=true- log intended action instead - Respect rate limits and API constraints
2.3 MCP Tool Invocation Pattern
Based on Context7 MCP documentation, MCP tools are invoked via JSON-RPC calls. When Claude Code has MCP servers configured, you reference them in your instructions:
Example Email Send:
Use the MCP email server to send an email:
- Server: email (configured in Claude Code MCP settings)
- Tool: send_email
- Parameters:
- to: client@example.com
- subject: Invoice #1234
- body: Please find attached your invoice.
- attachment: /Vault/Invoices/2026-01_Client_A.pdf
Example LinkedIn Post:
Use the MCP LinkedIn server to create a post:
- Server: linkedin (configured in Claude Code MCP settings)
- Tool: create_post
- Parameters:
- content: Excited to announce our new product launch...
- visibility: public
Note: Actual MCP invocation syntax depends on Claude Code's MCP integration. Refer to your MCP server documentation for exact tool names and parameters.
2.4 Handle Execution Results
After invoking MCP tool:
Capture result:
- Success: Note execution ID, timestamp, any returned data
- Failure: Capture error message, error code, and context
Update approval file:
- Add execution metadata (timestamp, result, MCP server used)
- Mark status as
executed(success) orfailed(error) - Move file to appropriate location (see Section 2.5)
Update related plan file:
- Mark relevant checkbox as completed
- Add execution note
- Update plan status if all actions completed
Log to audit log (MANDATORY):
- Create/update
/Logs/YYYY-MM-DD.json - Add entry with:
{ "timestamp": "2026-01-09T15:30:00Z", "action_type": "send_email", "actor": "claude_code", "target": "client@example.com", "parameters": { "subject": "Invoice #1234", "body_length": 150 }, "approval_status": "approved", "approved_by": "human", "mcp_server": "email", "mcp_tool": "send_email", "result": "success", "execution_id": "email_abc123", "error": null }
- Create/update
2.5 Move Processed Approval Files
After execution (success or failure):
On Success:
- Move file from
/Approved/to/Done/ - Add execution metadata to file
- Preserve original approval request content
- Move file from
On Failure:
- Move file from
/Approved/to/Rejected/(or create/Failed/folder) - Add error details to file
- Update Dashboard with failure notification
- Log failure in audit log
- Move file from
On Expired:
- Move expired files to
/Rejected/ - Add expiration note
- Do not execute expired approvals
- Move expired files to
2.6 Update Dashboard
After processing approved actions, update Dashboard.md:
- Pending Approvals Count: Number of files in
/Pending_Approval/ - Executed Today: Count of successfully executed actions
- Failed Actions: Count of failed executions
- Recent MCP Activity: Last MCP server invocations
- MCP Server Status: Health of configured MCP servers
3. File Structure Requirements
3.1 Approval Request File Format
Approval files in /Approved/ should follow this structure:
---
type: approval_request
action: send_email|post_linkedin|make_payment|browser_action
plan_id: /Plans/PLAN_email_response_2026-01-09.md
source_action_item: /Done/action-email-12345.md
created: 2026-01-09T10:30:00Z
expires: 2026-01-10T10:30:00Z
status: approved
priority: high
mcp_server: email
mcp_tool: send_email
---
## Action Request
**Action Type**: Send email reply
**Reason**: Client requested invoice via email
**Target**: client@example.com
**Subject**: Invoice #1234 - $1,500
## Parameters
- **To**: client@example.com
- **Subject**: Invoice #1234 - $1,500
- **Body**: Please find attached your invoice for January 2026.
- **Attachment**: /Vault/Invoices/2026-01_Client_A.pdf
## Execution Status
- Status: [pending|executing|executed|failed]
- Executed at: [ISO_TIMESTAMP]
- MCP Server: [server_name]
- Execution ID: [id_from_mcp]
- Error: [error_message if failed]
3.2 Audit Log Format
Audit logs in /Logs/YYYY-MM-DD.json:
{
"date": "2026-01-09",
"actions": [
{
"timestamp": "2026-01-09T15:30:00Z",
"action_type": "send_email",
"actor": "claude_code",
"target": "client@example.com",
"parameters": {
"subject": "Invoice #1234",
"body_length": 150
},
"approval_status": "approved",
"approved_by": "human",
"approval_file": "APPROVAL_email_client_2026-01-09.md",
"mcp_server": "email",
"mcp_tool": "send_email",
"result": "success",
"execution_id": "email_abc123",
"error": null,
"plan_reference": "/Plans/PLAN_email_response_2026-01-09.md"
}
]
}
4. Processing Workflow
Step-by-Step Process
Detect Approved Files
- Scan
/Approved/for.mdfiles - Filter out expired files (move to
/Rejected/) - Sort by priority and creation time
- Scan
Read and Validate
- Read approval file
- Read related plan file (if referenced)
- Read
Company_Handbook.mdfor MCP configuration - Verify all required parameters present
- Check MCP server availability
Execute via MCP
- Invoke appropriate MCP tool with parameters
- Handle dry-run mode (log without executing)
- Capture execution result
Handle Results
- MANDATORY: Log to audit log FIRST (
/Logs/YYYY-MM-DD.json) usingAuditLogger.log_execution() - CRITICAL: If audit logging fails, DO NOT move file - treat as critical error
- Update approval file with execution metadata
- Update related plan file (mark checkboxes)
- Move file to
/Done/(success) or/Rejected/(failure) ONLY after successful audit logging
- MANDATORY: Log to audit log FIRST (
Update Dashboard
- Update executed actions count
- Update MCP server status
- Show recent activity
Error Recovery
- On MCP server error: Log, move to
/Rejected/, notify via Dashboard - On parameter error: Log, add error to approval file, move to
/Rejected/ - On expired approval: Move to
/Rejected/, log expiration
- On MCP server error: Log, move to
5. Error Handling
Common Errors and Responses
MCP Server Not Available:
- Log error with server name
- Move approval file to
/Rejected/with error note - Update Dashboard with MCP server status
- Do not attempt retry (human must fix MCP configuration)
Missing Required Parameters:
- Log error with missing parameters
- Update approval file with error details
- Move to
/Rejected/folder - Suggest fixing approval file manually
Expired Approval:
- Log expiration notice
- Move to
/Rejected/without execution - Do not execute expired approvals (security)
MCP Tool Execution Failed:
- Log error from MCP server
- Update approval file with error details
- Move to
/Rejected/folder - Update Dashboard with failure notification
- Preserve original approval request for review
Rate Limit Exceeded:
- Log rate limit error
- Move approval back to
/Approved/(retry later) - Update Dashboard with retry status
- Wait before processing more actions
Dry Run Mode:
- Log intended action (do not execute)
- Mark as "dry_run" in audit log
- Update approval file with dry-run note
- Move to
/Done/with dry-run status
6. MCP Server Integration
Silver Tier MCP Servers
This skill integrates with three custom FastMCP servers implemented in Python:
| Server | Location | Tools | Purpose |
|---|---|---|---|
email-mcp |
AI_Employee/mcp_servers/email_mcp.py |
send_email, health_check |
Send emails via SMTP with TLS |
linkedin-mcp |
AI_Employee/mcp_servers/linkedin_mcp.py |
create_post, health_check |
Post to LinkedIn via API v2 |
playwright-mcp |
AI_Employee/mcp_servers/playwright_mcp.py |
browser_action, take_screenshot, health_check |
Browser automation |
MCP Server Configuration
MCP servers are configured via environment variables and run as stdio-based servers:
Email MCP Configuration (.env):
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=user@gmail.com
SMTP_PASSWORD=app_password
FROM_ADDRESS=user@gmail.com
SMTP_USE_TLS=true
LinkedIn MCP Configuration (.env):
LINKEDIN_ACCESS_TOKEN=AQV...
LINKEDIN_PERSON_URN=urn:li:person:ABCDEF123
LINKEDIN_API_VERSION=202601
Playwright MCP Configuration (.env):
PLAYWRIGHT_BROWSER=chromium
PLAYWRIGHT_HEADLESS=true
PLAYWRIGHT_SCREENSHOT_DIR=D:/AI_Employee/Logs/screenshots
PLAYWRIGHT_TIMEOUT_MS=30000
MCP Server Invocation Pattern
Step 1: Health Check Before Execution
Before invoking any MCP tool, ALWAYS run health_check to verify server availability:
# For email-mcp
from AI_Employee.mcp_servers.email_mcp import health_check as email_health
result = email_health()
if result['status'] != 'available':
# Handle MCP_SERVER_UNAVAILABLE error
# For linkedin-mcp
from AI_Employee.mcp_servers.linkedin_mcp import health_check as linkedin_health
result = linkedin_health()
if result['status'] != 'available':
# Handle AUTH_EXPIRED or NETWORK_ERROR
# For playwright-mcp
from AI_Employee.mcp_servers.playwright_mcp import health_check as playwright_health
result = playwright_health()
if result['status'] != 'available':
# Handle BROWSER_ERROR
Step 2: Invoke MCP Tool Based on Action Type
import time
from datetime import datetime, timezone
from pathlib import Path
from AI_Employee.mcp_servers.email_mcp import send_email
from AI_Employee.mcp_servers.linkedin_mcp import create_post
from AI_Employee.mcp_servers.playwright_mcp import browser_action
from AI_Employee.utils.audit_logger import AuditLogger
from AI_Employee.utils.sanitizer import CredentialSanitizer
from AI_Employee.models.approval_request import parse_approval_file
# Parse approval file
approval = parse_approval_file(Path('/Approved/APPROVAL_email_20260109.md'))
start_time = time.time()
# Route to correct MCP server based on action_type
if approval.action_type == 'email_send':
result = send_email(
to=approval.parameters.get('to'),
subject=approval.parameters.get('subject'),
body=approval.parameters.get('body'),
is_html=approval.parameters.get('is_html', False),
attachments=approval.parameters.get('attachments')
)
elif approval.action_type == 'linkedin_post':
result = create_post(
text=approval.parameters.get('text'),
visibility=approval.parameters.get('visibility', 'PUBLIC'),
hashtags=approval.parameters.get('hashtags')
)
elif approval.action_type == 'browser_action':
result = browser_action(
url=approval.parameters.get('url'),
action_type=approval.parameters.get('action_type', 'navigate'),
selector=approval.parameters.get('selector'),
value=approval.parameters.get('value'),
screenshot=True,
form_fields=approval.parameters.get('form_fields')
)
execution_duration_ms = int((time.time() - start_time) * 1000)
Step 3: Log Execution with AuditLogger (MANDATORY - MUST succeed before moving file)
CRITICAL REQUIREMENT: Audit logging is MANDATORY and must succeed before moving approval files. If logging fails, treat as critical error - DO NOT move file, DO NOT update plan, log error to console.
from AI_Employee.utils.audit_logger import AuditLogger
from AI_Employee.utils.sanitizer import CredentialSanitizer
# Initialize logger
sanitizer = CredentialSanitizer()
logger = AuditLogger(logs_path=Path('AI_Employee/Logs'), sanitizer=sanitizer)
# Log execution (parameters are auto-sanitized)
# MUST catch exceptions - if this fails, do not proceed with file movement
try:
entry_id = logger.log_execution(
action_type=approval.action_type, # 'email_send', 'linkedin_post', 'browser_action'
actor='claude-code',
target=approval.target,
parameters=approval.parameters, # Will be sanitized automatically
approval_status='approved',
approval_by='user',
approval_timestamp=approval.approval_timestamp,
mcp_server=approval.mcp_server,
result='success' if result.get('status') in ['sent', 'published', 'success'] else 'failure',
error=result.get('error'),
error_code=result.get('error_code'),
execution_duration_ms=execution_duration_ms,
approval_request_id=approval.id,
extra_fields={
'message_id': result.get('message_id'),
'post_id': result.get('post_id'),
'post_url': result.get('post_url'),
'screenshot_path': result.get('screenshot_path')
}
)
# Logging succeeded - proceed with file movement
except Exception as log_error:
# CRITICAL: If audit logging fails, do NOT move file
print(f"CRITICAL ERROR: Audit logging failed: {log_error}")
print(f"Approval file NOT moved - manual intervention required: {approval_file_path}")
raise # Re-raise to prevent file movement
)
LinkedIn Posting Rules Enforcement (T054/T055)
Before executing LinkedIn posts, ALWAYS check posting rules:
from AI_Employee.utils.linkedin_rules import LinkedInPostingRules, get_linkedin_metrics
# Initialize rules enforcer
rules = LinkedInPostingRules(
logs_path=Path('AI_Employee/Logs'),
max_posts_per_day=3, # From Company_Handbook.md
posting_schedule_start=9, # 9 AM
posting_schedule_end=17 # 5 PM
)
# Check if posting is allowed
can_post, block_reason = rules.can_post_now()
if not can_post:
if 'rate_limit_daily_exceeded' in block_reason:
# T054: Daily limit reached - queue for next day
# Log rate_limit_daily_exceeded to audit
logger.log_execution(
action_type='linkedin_post',
actor='system',
target='LinkedIn',
result='failure',
error=block_reason,
error_code='RATE_LIMIT_DAILY_EXCEEDED'
)
# Keep approval file in /Approved/ with queued status
# Add queue_until timestamp to file
elif 'outside_posting_schedule' in block_reason:
# T055: Outside business hours - queue for next window
next_window = rules.get_next_posting_window()
# Keep approval file with estimated post time
# Update approval file with queue status
Daily Post Limit Check (T054):
posts_today = rules.count_linkedin_posts_today()
max_posts = rules.max_posts_per_day # 3 by default
if posts_today >= max_posts:
# Queue post for tomorrow
# Log: rate_limit_daily_exceeded
# DO NOT execute
# Keep in /Approved/ with status: queued
Posting Schedule Check (T055):
if not rules.is_within_posting_schedule():
# Current time outside 9am-5pm
next_window = rules.get_next_posting_window()
# Queue post for next business hour
# Keep in /Approved/ with estimated_post_time
Queue Behavior:
- Posts that violate rules are NOT rejected
- Posts remain in
/Approved/withstatus: queued - Add
queue_until: {ISO_TIMESTAMP}to approval file - Orchestrator re-checks queued posts on each cycle
- Execute when rules permit (next day or next business hour)
Error Codes and Handling
Each MCP server returns specific error codes:
Email MCP Error Codes:
| Error Code | Description | Recovery |
|---|---|---|
SMTP_AUTH_FAILED |
Invalid credentials | Check SMTP_USERNAME/PASSWORD |
SMTP_CONNECTION_ERROR |
Cannot connect to SMTP | Check SMTP_HOST/PORT |
INVALID_RECIPIENT |
Bad email address | Verify recipient |
ATTACHMENT_TOO_LARGE |
>25MB attachments | Reduce attachment size |
LinkedIn MCP Error Codes:
| Error Code | Description | Recovery |
|---|---|---|
AUTH_EXPIRED |
Token expired (60 days) | Re-authenticate OAuth, create /Needs_Action/ notification |
RATE_LIMIT_EXCEEDED |
Too many posts | Wait and retry |
RATE_LIMIT_DAILY_EXCEEDED |
Daily limit (3) reached | Queue for next day |
OUTSIDE_POSTING_SCHEDULE |
Outside business hours | Queue for next window |
INVALID_CONTENT |
Policy violation | Review post content |
NETWORK_ERROR |
API unreachable | Check connectivity |
LinkedIn AUTH_EXPIRED Handling (T057)
When LinkedIn MCP returns AUTH_EXPIRED error:
from AI_Employee.utils.linkedin_rules import (
handle_linkedin_auth_expired,
check_linkedin_auth_error
)
# After MCP invocation returns error
result = linkedin_mcp.create_post(text=post_text, visibility='PUBLIC')
if result.get('status') == 'error':
error_code = result.get('error_code')
error_message = result.get('error')
if check_linkedin_auth_error(error_code, error_message):
# T057: Create notification in /Needs_Action/
notification_path = handle_linkedin_auth_expired(
needs_action_path=Path('AI_Employee/Needs_Action'),
approval_file_path=approval_file_path, # Keep in /Approved/
error_message=error_message
)
# Log to audit
logger.log_execution(
action_type='linkedin_post',
actor='system',
target='LinkedIn',
result='failure',
error=error_message,
error_code='AUTH_EXPIRED',
extra_fields={
'notification_created': str(notification_path),
'retry_after_credential_refresh': True
}
)
# DO NOT move approval file to /Rejected/
# Keep in /Approved/ for retry after credentials are refreshed
AUTH_EXPIRED Flow:
- Detect AUTH_EXPIRED from MCP response
- Create notification in
/Needs_Action/with refresh instructions - Log failure to audit with
retry_after_credential_refresh: true - Keep approval file in
/Approved/- do NOT reject - User refreshes credentials per notification instructions
- Next orchestrator cycle retries the post
Notification Contents:
- Clear instructions for refreshing LinkedIn OAuth token
- Link to LinkedIn Developer Portal
- Required OAuth scopes
- Steps to update
.envfile - Reference to failed approval file
Playwright MCP Error Codes:
| Error Code | Description | Recovery |
|---|---|---|
BROWSER_ERROR |
Chromium not installed | Run: playwright install chromium |
SELECTOR_NOT_FOUND |
Element not found | Verify CSS selector |
TIMEOUT |
Page load timeout | Increase PLAYWRIGHT_TIMEOUT_MS |
NAVIGATION_ERROR |
Page navigation failed | Verify URL |
Handling MCP Errors in Execution Flow
# Error handling pattern
def execute_approved_action(approval_file_path: Path) -> dict:
from AI_Employee.models.approval_request import parse_approval_file
from AI_Employee.utils.audit_logger import AuditLogger
approval = parse_approval_file(approval_file_path)
logs_path = Path('AI_Employee/Logs')
logger = AuditLogger(logs_path)
# 1. Check expiration
if approval.created_timestamp and approval.approval_timestamp:
age_hours = (datetime.now() - approval.created_timestamp).total_seconds() / 3600
if age_hours > 24:
# Move to /Rejected/, log expiration
logger.log_execution(
action_type=approval.action_type,
actor='system',
target=approval.target,
result='failure',
error='Approval request expired (>24 hours)',
error_code='EXPIRED'
)
# Move file to /Rejected/
return {'status': 'error', 'error': 'Expired', 'error_code': 'EXPIRED'}
# 2. Health check MCP server
health_result = _check_mcp_health(approval.mcp_server)
if health_result['status'] != 'available':
logger.log_execution(
action_type=approval.action_type,
actor='system',
target=approval.target,
mcp_server=approval.mcp_server,
result='failure',
error=f"MCP server unavailable: {health_result.get('error', 'Unknown')}",
error_code='MCP_SERVER_UNAVAILABLE'
)
# Move file to /Rejected/
# Create notification in /Needs_Action/
return {'status': 'error', 'error_code': 'MCP_SERVER_UNAVAILABLE'}
# 3. Validate parameters
validation_error = _validate_parameters(approval)
if validation_error:
logger.log_execution(
action_type=approval.action_type,
actor='system',
target=approval.target,
result='failure',
error=validation_error,
error_code='PARAMETER_VALIDATION_FAILED'
)
# Move file to /Rejected/
return {'status': 'error', 'error_code': 'PARAMETER_VALIDATION_FAILED'}
# 4. Execute via MCP
start_time = time.time()
try:
result = _invoke_mcp_tool(approval)
execution_duration_ms = int((time.time() - start_time) * 1000)
if result.get('status') in ['sent', 'published', 'success']:
# Success: Log, move to /Done/
logger.log_execution(
action_type=approval.action_type,
actor='claude-code',
target=approval.target,
parameters=approval.parameters,
approval_status='approved',
approval_by='user',
mcp_server=approval.mcp_server,
result='success',
execution_duration_ms=execution_duration_ms,
approval_request_id=approval.id
)
# Move file to /Done/
# Update related plan file
return result
else:
# Failure: Log, move to /Rejected/
logger.log_execution(
action_type=approval.action_type,
actor='claude-code',
target=approval.target,
mcp_server=approval.mcp_server,
result='failure',
error=result.get('error'),
error_code=result.get('error_code', 'MCP_TOOL_FAILED'),
execution_duration_ms=execution_duration_ms
)
# Move file to /Rejected/
# Create notification in /Needs_Action/
return result
except Exception as e:
logger.log_execution(
action_type=approval.action_type,
actor='system',
target=approval.target,
mcp_server=approval.mcp_server,
result='failure',
error=str(e),
error_code='UNKNOWN'
)
return {'status': 'error', 'error': str(e), 'error_code': 'UNKNOWN'}
Credential Sanitization
CRITICAL: Always sanitize parameters before logging:
from AI_Employee.utils.sanitizer import CredentialSanitizer
sanitizer = CredentialSanitizer()
# Sensitive keys automatically detected and redacted:
# password, token, api_key, secret, credential, auth, bearer,
# smtp_password, access_token, refresh_token, private_key,
# client_secret, authorization
# Token-like strings (>30 chars alphanumeric) are masked as "first4...last4"
# Example:
params = {
'to': 'user@example.com',
'smtp_password': 'secret123', # Will be redacted
'body': 'Hello world'
}
sanitized = sanitizer.sanitize(params)
# Result: {'to': 'user@example.com', 'smtp_password': '***REDACTED***', 'body': 'Hello world'}
Update Related Plan File After Execution
After successful execution, update the related plan file:
import re
from pathlib import Path
from datetime import datetime, timezone
def update_plan_file(plan_path: Path, action_description: str, result: dict):
"""Mark checkbox as completed and add execution note."""
if not plan_path.exists():
return
content = plan_path.read_text(encoding='utf-8')
timestamp = datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M')
# Find matching unchecked checkbox containing the action description
pattern = rf'- \[ \] (.*{re.escape(action_description[:30])}.*)'
def replace_checkbox(match):
original_text = match.group(1)
execution_note = f"\n - Executed: {timestamp}"
if result.get('message_id'):
execution_note += f"\n - Message ID: {result['message_id']}"
if result.get('post_url'):
execution_note += f"\n - Post URL: {result['post_url']}"
return f"- [x] {original_text}{execution_note}"
updated_content = re.sub(pattern, replace_checkbox, content, count=1)
plan_path.write_text(updated_content, encoding='utf-8')
Verifying MCP Server Availability
Before invoking MCP tools, check if servers are available:
- Check environment variables are configured
- Run health_check tool for the target MCP server
- Verify returned status is 'available'
- Log server status in Dashboard
If MCP server unavailable:
- Log error with error code
MCP_SERVER_UNAVAILABLE - Move approval to
/Rejected/with "MCP server unavailable" note - Create notification in
/Needs_Action/with troubleshooting steps - Do not attempt execution
7. Security and Safety
Mandatory Checks Before Execution
Approval Verification:
- File MUST be in
/Approved/folder (never execute from/Pending_Approval/) - Approval file MUST have
status: approvedin frontmatter - Approval MUST not be expired
- File MUST be in
Parameter Validation:
- All required parameters MUST be present
- Parameter values MUST be within acceptable ranges (check Company_Handbook.md)
- Sensitive data (passwords, tokens) MUST NOT be in approval files
Rate Limiting:
- Check daily/hourly action limits from Company_Handbook.md
- Do not exceed rate limits
- Queue actions if limit reached
Dry Run Mode:
- If
DRY_RUN=true, log intended action but do not execute - Mark execution as "dry_run" in audit log
- If
Audit Logging Requirements
MANDATORY for Silver tier - every execution MUST be logged:
- Timestamp (ISO 8601)
- Action type and target
- Parameters (sanitized - no credentials)
- Approval status and approver
- MCP server and tool used
- Execution result (success/failure)
- Error details (if failed)
- Related plan and source item references
8. Testing the Skill
To test this skill:
Create test approval request:
# Create approval file in /Approved/ cat > vault/Approved/APPROVAL_test_email.md << 'EOF' --- type: approval_request action: send_email status: approved mcp_server: email mcp_tool: send_email --- ## Parameters - To: test@example.com - Subject: Test Email - Body: This is a test email. EOFInvoke Claude Code with prompt:
@execute-approved-actionsor
Execute any approved actions in /Approved folder. Invoke MCP servers as needed and log all executions.Verify:
- MCP tool invoked (check logs or MCP server output)
- Approval file moved to
/Done/ - Audit log entry created in
/Logs/YYYY-MM-DD.json - Dashboard updated
9. Example Usage
User Prompt:
Check /Approved folder and execute any approved actions using MCP servers. Log all executions to audit logs.
Skill Execution:
- Reads
/Approved/APPROVAL_email_client_a.md - Verifies approval status and parameters
- Invokes MCP email server tool
send_emailwith parameters - Captures execution result (success)
- Updates approval file with execution metadata
- Logs to
/Logs/2026-01-09.json - Moves approval file to
/Done/ - Updates related plan file
- Updates Dashboard.md
Expected Output:
- Approval file executed via MCP server
- File moved to
/Done/ - Audit log entry created
- Dashboard updated with execution status
- Related plan updated
10. Best Practices
Do:
- Always verify approval file is in
/Approved/folder before execution - Check expiration dates (never execute expired)
- Log every execution attempt (success or failure)
- Update related plan files after execution
- Handle MCP errors gracefully (don't crash)
- Respect rate limits and API constraints
- Sanitize sensitive data in audit logs
Don't:
- Execute actions from
/Pending_Approval/(not yet approved) - Skip audit logging (mandatory for Silver tier)
- Execute expired approvals
- Retry failed executions automatically (human review needed)
- Expose credentials in audit logs
- Execute if MCP server unavailable (log and reject)
- Bypass approval workflow for "quick" actions
11. Integration with Other Skills
This skill works with:
@process-action-items: Creates approval requests that this skill executes@schedule-operations: Processes scheduled actions that have been approved@update-dashboard: Updates dashboard with execution status
By following this skill, you act as a safe and reliable action executor:
- Only executing human-approved actions,
- Logging all executions for audit and compliance,
- Integrating with MCP servers for external capabilities,
- Maintaining system integrity through proper error handling,
- And enabling Silver tier autonomous operation with human oversight.