twentyhq/twenty · error · RuntimeError
Twenty MCP bridge not available. Missing requests library or
Error message
Twenty MCP bridge not available. Missing requests library or credentials.
What it means
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.
Source
Thrown at packages/twenty-server/src/engine/core-modules/tool/tools/code-interpreter-tool/twenty-mcp-helper.const.ts:71
Catalog tools (find_many_companies, create_one_person, …) are routed
through execute_tool. MCP-native tools are called directly.
The execute_tool envelope { success, message, result } is
unwrapped so you always get the inner tool's result back.
Args:
name: Tool name (catalog or MCP-native)
arguments: Tool arguments as a dictionary
Returns:
Tool result as parsed JSON
Example:
companies = twenty.call_tool('find_many_companies', {'limit': 5})
# companies == {'records': [...], 'count': '5'}
"""
if not self._available:
raise RuntimeError('Twenty MCP bridge not available. Missing requests library or credentials.')
if name in self._MCP_NATIVE_TOOLS:
return self._raw_mcp_call(name, arguments)
wrapped = self._raw_mcp_call('execute_tool', {
'toolName': name,
'arguments': arguments or {},
})
# execute_tool returns one of:
# success: { success: True, message, result: {...} }
# failure: { success: False, message, error }
# Raise on failure, unwrap on success, pass through unknown shapes.
if isinstance(wrapped, dict):
if wrapped.get('success') is False:
raise Exception(wrapped.get('error') or wrapped.get('message') or
f"execute_tool failed for {name}")
if 'result' in wrapped:
return wrapped['result']View on GitHub (pinned to 1f5dd2bbd2)
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.
Example fix
# before
companies = twenty.call_tool('find_many_companies', {'limit': 5}) # raises if bridge unconfigured
# after — check availability first, degrade gracefully
if not twenty.available:
raise RuntimeError('Twenty MCP bridge unavailable: set TWENTY_SERVER_URL and TWENTY_API_TOKEN and install requests')
companies = twenty.call_tool('find_many_companies', {'limit': 5}) Defensive patterns
Strategy: type-guard
Validate before calling
# Run inside the sandbox before any tool call.
if not twenty.available:
missing = []
try:
import requests # noqa: F401
except ImportError:
missing.append('requests package')
if not os.environ.get('TWENTY_SERVER_URL'):
missing.append('TWENTY_SERVER_URL')
if not os.environ.get('TWENTY_API_TOKEN'):
missing.append('TWENTY_API_TOKEN')
raise RuntimeError(f'Twenty MCP bridge unavailable; missing: {", ".join(missing)}') Type guard
# The helper exposes `available` as the canonical guard.
def twenty_ready() -> bool:
return bool(getattr(twenty, 'available', False)) Try / catch
try:
result = twenty.call_tool('find_many_companies', {'limit': 5})
except RuntimeError as e:
if 'not available' in str(e):
# degrade gracefully: skip the tool call, return empty, log the missing config
result = {'records': [], 'count': '0', '_bridge_unavailable': True}
else:
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- execute_tool failed for {name}
- MCP Error: {result['error'].get('message', 'Unknown error')}
- {input_dir} is not a directory
- {output_file} must be a .docx, .pptx, or .xlsx file
- Slide index {idx} out of range (0-{total_slides - 1})
AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12).
Data as JSON: /api/errors/8e4213979b66d976.
Report an issue: GitHub.