{"record":{"id":"8e4213979b66d976","repo":"twentyhq/twenty","slug":"twenty-mcp-bridge-not-available-missing-requests","errorCode":null,"errorMessage":"Twenty MCP bridge not available. Missing requests library or credentials.","messagePattern":"Twenty MCP bridge not available\\. Missing requests library or credentials\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"packages/twenty-server/src/engine/core-modules/tool/tools/code-interpreter-tool/twenty-mcp-helper.const.ts","lineNumber":71,"sourceCode":"\n        Catalog tools (find_many_companies, create_one_person, …) are routed\n        through execute_tool. MCP-native tools are called directly.\n        The execute_tool envelope { success, message, result } is\n        unwrapped so you always get the inner tool's result back.\n\n        Args:\n            name: Tool name (catalog or MCP-native)\n            arguments: Tool arguments as a dictionary\n\n        Returns:\n            Tool result as parsed JSON\n\n        Example:\n            companies = twenty.call_tool('find_many_companies', {'limit': 5})\n            # companies == {'records': [...], 'count': '5'}\n        \"\"\"\n        if not self._available:\n            raise RuntimeError('Twenty MCP bridge not available. Missing requests library or credentials.')\n\n        if name in self._MCP_NATIVE_TOOLS:\n            return self._raw_mcp_call(name, arguments)\n\n        wrapped = self._raw_mcp_call('execute_tool', {\n            'toolName': name,\n            'arguments': arguments or {},\n        })\n        # execute_tool returns one of:\n        #   success: { success: True,  message, result: {...} }\n        #   failure: { success: False, message, error }\n        # Raise on failure, unwrap on success, pass through unknown shapes.\n        if isinstance(wrapped, dict):\n            if wrapped.get('success') is False:\n                raise Exception(wrapped.get('error') or wrapped.get('message') or\n                                f\"execute_tool failed for {name}\")\n            if 'result' in wrapped:\n                return wrapped['result']","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/twentyhq/twenty/blob/1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6/packages/twenty-server/src/engine/core-modules/tool/tools/code-interpreter-tool/twenty-mcp-helper.const.ts#L53-L89","documentation":"Raised by TwentyMCP.call_tool (the Python helper injected into code-interpreter sandboxes) when self._available is False. Availability is computed once in __init__ as the AND of three conditions: the `requests` Python package imported successfully, TWENTY_SERVER_URL is set and non-empty, and TWENTY_API_TOKEN is set and non-empty. The message names the two credential/env causes; the requests cause is implicit.","triggerScenarios":"Running sandboxed code that calls twenty.call_tool(...) when the sandbox environment was not provisioned with TWENTY_SERVER_URL and/or TWENTY_API_TOKEN, or when the `requests` package is not installed in the sandbox's Python. Any call_tool, bulk_upsert, or lookup_by on the global `twenty` instance will trip this on the first invocation.","commonSituations":"Local/dev runs of the code-interpreter tool without the API token configured; a deployment where the sandbox sidecar does not inherit the server's env vars; a sandbox image stripped of the requests package to reduce size; a token rotation that left the sandbox with an empty value.","solutions":["Set both env vars in the sandbox process: TWENTY_SERVER_URL (e.g. http://localhost:3000) and TWENTY_API_TOKEN (a valid API key).","Ensure the `requests` package is installed in the sandbox Python environment (`pip install requests`).","Guard with `if twenty.available:` before calling tools, and log a clear message when the bridge is off.","Verify the server actually exposes /mcp and the token has scope to call tools before relying on the bridge."],"exampleFix":"# before\ncompanies = twenty.call_tool('find_many_companies', {'limit': 5})   # raises if bridge unconfigured\n# after — check availability first, degrade gracefully\nif not twenty.available:\n    raise RuntimeError('Twenty MCP bridge unavailable: set TWENTY_SERVER_URL and TWENTY_API_TOKEN and install requests')\ncompanies = twenty.call_tool('find_many_companies', {'limit': 5})","handlingStrategy":"type-guard","validationCode":"# Run inside the sandbox before any tool call.\nif not twenty.available:\n    missing = []\n    try:\n        import requests  # noqa: F401\n    except ImportError:\n        missing.append('requests package')\n    if not os.environ.get('TWENTY_SERVER_URL'):\n        missing.append('TWENTY_SERVER_URL')\n    if not os.environ.get('TWENTY_API_TOKEN'):\n        missing.append('TWENTY_API_TOKEN')\n    raise RuntimeError(f'Twenty MCP bridge unavailable; missing: {\", \".join(missing)}')","typeGuard":"# The helper exposes `available` as the canonical guard.\ndef twenty_ready() -> bool:\n    return bool(getattr(twenty, 'available', False))","tryCatchPattern":"try:\n    result = twenty.call_tool('find_many_companies', {'limit': 5})\nexcept RuntimeError as e:\n    if 'not available' in str(e):\n        # degrade gracefully: skip the tool call, return empty, log the missing config\n        result = {'records': [], 'count': '0', '_bridge_unavailable': True}\n    else:\n        raise","preventionTips":["Always branch on `twenty.available` before the first call rather than catching after.","In sandbox setup scripts, assert requests is installed and both env vars are set before user code runs.","After token rotation, redeploy the sandbox sidecar so it picks up the new env."],"tags":["python","code-interpreter","mcp","env-configuration","twenty-api"],"backgroundTag":null,"analyzedSha":"1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6","analyzedAt":"2026-08-12T15:37:27.593Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}