From 42dbdcde9e3f2150ee17a15207a917fe1616d150 Mon Sep 17 00:00:00 2001 From: daniel Date: Fri, 15 May 2026 23:32:23 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20factbase-cloud=20integration=20?= =?UTF-8?q?=E2=80=94=20knowledge=5Fagent=20subagent=20with=20M2M=20auth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agentclaw/app/agent_claw_main/config.py | 8 +++++- agentclaw/app/agent_claw_main/main.py | 37 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/agentclaw/app/agent_claw_main/config.py b/agentclaw/app/agent_claw_main/config.py index b143d5f..f5325a1 100644 --- a/agentclaw/app/agent_claw_main/config.py +++ b/agentclaw/app/agent_claw_main/config.py @@ -6,6 +6,9 @@ _DEFAULTS = { '/agent-claw/model-id': 'us.anthropic.claude-sonnet-4-6', '/agent-claw/config/compaction_model_id': 'us.anthropic.claude-3-5-haiku-20241022-v1:0', '/agent-claw/aws-mcp-url': 'https://aws-mcp.us-east-1.api.aws/mcp', + '/agent-claw/factbase-cloud/client-id': '', + '/agent-claw/factbase-cloud/client-secret': '', + '/agent-claw/factbase-cloud/mcp-url': '', } @@ -13,7 +16,7 @@ def _load(): ssm = boto3.client('ssm', region_name='us-east-1') names = list(_DEFAULTS.keys()) try: - resp = ssm.get_parameters(Names=names) + resp = ssm.get_parameters(Names=names, WithDecryption=True) found = {p['Name']: p['Value'] for p in resp['Parameters']} except Exception: found = {} @@ -25,3 +28,6 @@ _params = _load() AGENT_MODEL_ID: str = _params['/agent-claw/model-id'] COMPACTION_MODEL_ID: str = _params['/agent-claw/config/compaction_model_id'] AWS_MCP_URL: str = _params['/agent-claw/aws-mcp-url'] +FACTBASE_CLOUD_CLIENT_ID: str = _params.get('/agent-claw/factbase-cloud/client-id', '') +FACTBASE_CLOUD_CLIENT_SECRET: str = _params.get('/agent-claw/factbase-cloud/client-secret', '') +FACTBASE_CLOUD_MCP_URL: str = _params.get('/agent-claw/factbase-cloud/mcp-url', '') diff --git a/agentclaw/app/agent_claw_main/main.py b/agentclaw/app/agent_claw_main/main.py index b591f10..b7764cf 100644 --- a/agentclaw/app/agent_claw_main/main.py +++ b/agentclaw/app/agent_claw_main/main.py @@ -4,6 +4,7 @@ agent-claw Runtime 1 — main assistant agent. Entrypoint for AgentCore CodeZip deployment. """ import os +import time from strands import Agent, tool from strands.models import BedrockModel from bedrock_agentcore.runtime import BedrockAgentCoreApp @@ -88,12 +89,48 @@ except Exception as _e: print(traceback.format_exc()) +# ── factbase-cloud token cache ──────────────────────────────────────────── +_fc_token: str = '' +_fc_token_expiry: float = 0.0 + +def _get_factbase_cloud_token() -> str: + """Fetch/cache a Cognito M2M token for factbase-cloud. 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.FACTBASE_CLOUD_CLIENT_ID or not config.FACTBASE_CLOUD_CLIENT_SECRET: + raise RuntimeError('factbase-cloud 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.FACTBASE_CLOUD_CLIENT_ID, + 'client_secret': config.FACTBASE_CLOUD_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] factbase-cloud 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"))], "coding": lambda: [MCPClient(lambda: aws_iam_streamablehttp_client(config.AWS_MCP_URL, aws_service="aws-mcp")), run_code], "documents": lambda: [http_request, file_read], + "factbase": lambda: [MCPClient(lambda: streamablehttp_client( + url=config.FACTBASE_CLOUD_MCP_URL, + headers={"Authorization": f"Bearer {_get_factbase_cloud_token()}"}, + ))], }