refactor: move factcloud from hardcoded SSM to per-user DynamoDB oauth2_m2m connection
- Add oauth2_m2m auth type to mcp_loader.py (client_secret in record, not SSM) - Remove _get_factcloud_token(), FACTCLOUD_* config, factcloud_clients from main.py - Seed Daniel's factcloud connection into enrolled_services.mcp_connections - factcloud now loaded dynamically via mcp_loader at session start
This commit is contained in:
@@ -89,39 +89,8 @@ except Exception as _e:
|
||||
print(traceback.format_exc())
|
||||
|
||||
|
||||
# ── factcloud token cache ────────────────────────────────────────────────
|
||||
_fc_token: str = ''
|
||||
_fc_token_expiry: float = 0.0
|
||||
|
||||
def _get_factcloud_token() -> str:
|
||||
"""Fetch/cache a Cognito M2M token for factcloud. Thread-safe for Lambda."""
|
||||
global _fc_token, _fc_token_expiry
|
||||
if _fc_token and time.time() < _fc_token_expiry - 60:
|
||||
return _fc_token
|
||||
if not config.FACTCLOUD_CLIENT_ID or not config.FACTCLOUD_CLIENT_SECRET:
|
||||
raise RuntimeError('factcloud credentials not configured in SSM')
|
||||
resp = httpx.post(
|
||||
'https://factbase-cloud.auth.us-east-1.amazoncognito.com/oauth2/token',
|
||||
data={
|
||||
'grant_type': 'client_credentials',
|
||||
'client_id': config.FACTCLOUD_CLIENT_ID,
|
||||
'client_secret': config.FACTCLOUD_CLIENT_SECRET,
|
||||
'scope': 'factbase-cloud/read factbase-cloud/write',
|
||||
},
|
||||
headers={'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
timeout=10,
|
||||
)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
_fc_token = data['access_token']
|
||||
_fc_token_expiry = time.time() + data.get('expires_in', 3600)
|
||||
print(f'[main] factcloud token refreshed, expires in {data.get("expires_in", 3600)}s')
|
||||
return _fc_token
|
||||
|
||||
|
||||
# ── Subagent loading ──────────────────────────────────────────────────────
|
||||
|
||||
from mcp.client.streamable_http import streamablehttp_client
|
||||
|
||||
TOOL_PRESETS = {
|
||||
"aws": lambda: [MCPClient(lambda: aws_iam_streamablehttp_client(config.AWS_MCP_URL, aws_service="aws-mcp"))],
|
||||
@@ -686,7 +655,7 @@ async def main(payload: dict, context):
|
||||
system_prompt = system_prompt + '\n\n---\n\n' + ltm_block
|
||||
|
||||
system_prompt += '\nAWS tools available: call_aws (any AWS API via AWS MCP Server), aws_list_lambda_functions, aws_get_cost_and_usage, aws_describe_service. Use call_aws directly for AWS API calls — do NOT say you lack AWS access.'
|
||||
system_prompt += '\n\nSubagents available — use them aggressively to save cost and improve quality:\n- aws_agent: all AWS infrastructure, cost, resource, IAM, CloudWatch queries\n- coding_agent: code writing, builds, deployments, CodeBuild/AppRunner/ECR\n- document_agent: summarize URLs, extract data from documents, process long text\nYou also have direct access to factcloud MCP tools (your personal knowledge graph) — use them directly for any factbase, factcloud, or knowledge base queries. Do NOT say you lack access to factcloud.\nDefault to delegating to subagents; only answer directly for simple conversational responses or tasks that don\'t fit a subagent.'
|
||||
system_prompt += '\n\nSubagents available — use them aggressively to save cost and improve quality:\n- aws_agent: all AWS infrastructure, cost, resource, IAM, CloudWatch queries\n- coding_agent: code writing, builds, deployments, CodeBuild/AppRunner/ECR\n- document_agent: summarize URLs, extract data from documents, process long text\nYou also have direct access to factcloud MCP tools (your personal knowledge graph) loaded from your MCP connections — use them directly for any factbase, factcloud, or knowledge base queries. Do NOT say you lack access to factcloud.\nDefault to delegating to subagents; only answer directly for simple conversational responses or tasks that don\'t fit a subagent.'
|
||||
|
||||
# Model: claude-sonnet-4-6 via cross-region inference
|
||||
# NOTE: extended thinking disabled — causes retry/duplicate issues with streaming
|
||||
@@ -704,17 +673,6 @@ async def main(payload: dict, context):
|
||||
run_code, send_file, request_iam_permission, apply_iam_permission,
|
||||
aws_list_lambda_functions, aws_get_cost_and_usage, aws_describe_service]
|
||||
|
||||
# factcloud: direct MCP connection (M2M Cognito auth)
|
||||
factcloud_clients = []
|
||||
if config.FACTCLOUD_MCP_URL:
|
||||
try:
|
||||
factcloud_clients = [MCPClient(lambda: streamablehttp_client(
|
||||
url=config.FACTCLOUD_MCP_URL,
|
||||
headers={"Authorization": f"Bearer {_get_factcloud_token()}"},
|
||||
))]
|
||||
except Exception as e:
|
||||
print(f'[main] factcloud connection failed: {e}')
|
||||
|
||||
# Load user's dynamic MCP connections
|
||||
mcp_connections = services.get('mcp_connections', [])
|
||||
mcp_clients, _mcp_to_close = mcp_loader.load_mcp_tools(mcp_connections, actor_id)
|
||||
@@ -723,7 +681,7 @@ async def main(payload: dict, context):
|
||||
ssm = boto3.client('ssm', region_name='us-east-1')
|
||||
subagent_tools = _load_subagents(ssm)
|
||||
|
||||
all_tools = base_tools + factcloud_clients + _aws_mcp_tools + mcp_clients + subagent_tools
|
||||
all_tools = base_tools + _aws_mcp_tools + mcp_clients + subagent_tools
|
||||
|
||||
agent = Agent(
|
||||
model=model,
|
||||
|
||||
Reference in New Issue
Block a user