Code Execution Server¶
agora_workbench.code_execution.server
¶
Base Code Execution Server for MCP.
This module provides a base class for creating MCP servers that execute Python code in isolated environments with domain-specific packages.
CodeExecutionServer(server_config, tool_registry=None, session_manager=None, auth_config=None, working_dir=None, tool_search_backend=None, publishers=None, skills=None, states=None, catalog=None)
¶
Bases: BaseMCPServer
Base class for MCP code execution servers.
Subclass this to create execution environments with specific Python interpreters and package sets. Each server exposes an MCP tool for executing code in its isolated environment.
Security features: - Timeout enforcement - Separate process execution - Optional working directory isolation - Configurable resource limits (override in subclass) - Pluggable authentication (Entra ID, no-op/dev, or custom) - Managed identity for downstream Azure resource access
Middleware Architecture: The server uses two layers of middleware at different levels of the stack:
Starlette Middleware (ASGI level, outermost layer): Applied to the entire HTTP application before FastMCP processing. Added in _create_middleware() and registered via app.add_middleware() during serve().
- MCPSessionMiddleware (outermost):
- Extracts Mcp-Session-Id header from requests to /mcp endpoint
- Stores session ID in ASGI scope for downstream access
-
Enables session correlation across tool calls
-
AuthMiddleware (inner):
- Validates Bearer tokens on /mcp and /object-transfer/* endpoints using Entra ID
- Extracts user identity (oid/sub) and token claims
- Stores authenticated context (token, claims, user_identity)
- Bypasses /health and /.well-known/* endpoints (no auth required)
- Returns RFC 9728 WWW-Authenticate header on 401 for OAuth discovery
FastMCP Middleware (tool call level, innermost layer): Applied to MCP tool calls after HTTP auth but before Pydantic validation. Added directly to the FastMCP instance:
- AssetResolutionMiddleware (added during init):
- Resolves tagged asset references like
id to local cache paths - Runs BEFORE Pydantic validation so Path parameters receive proper values
Full execution flow: HTTP Request → MCPSessionMiddleware → AuthMiddleware → FastMCP routing → AssetResolutionMiddleware → Pydantic validation → Tool callback
Constraints: - Asset resolution happens first, so asset parameters are properly typed
Asset/Handle Injection Patterns: The server supports two different patterns for injecting assets and handles:
- Auto-Extraction (execute_code_tool):
- Handle IDs (h_xxxxxxxxxxxx) and asset tags (
id ) embedded as string literals in code are automatically detected via AST analysis. - Matched literals are replaced with synthetic variables that hold the resolved object (for handles) or a Path to the cached file (for assets).
-
Validation: References must appear as complete string literals in assignments, function arguments, return statements, or container literals (list/dict/tuple/set). Unresolvable references fail fast.
-
Middleware-Based Injection (domain tools):
- Assets embedded in natural parameter values: grid_file="
xyz " - Middleware extracts, validates, and resolves before Pydantic validation
- Use case: LLM-driven tool calls where assets are encoded in argument values
- Validation: AssetResolutionMiddleware
Initialize the code execution server.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
server_config
|
ServerConfig
|
Server configuration (environment, assets, execution policy, features) |
required |
tool_registry
|
Optional[ToolRegistry]
|
Optional ToolRegistry containing domain-specific tools |
None
|
session_manager
|
Optional[SessionManager]
|
Optional SessionManager for stateful tool support (auto-created with defaults if None) |
None
|
auth_config
|
Optional[AuthConfig]
|
Authentication configuration providing token validation, identity extraction, and credential provisioning. |
None
|
working_dir
|
Optional[Path]
|
Working directory for code execution (None = temp dir per execution) |
None
|
tool_search_backend
|
Optional[ToolSearchBackend]
|
Optional pre-configured ToolSearchBackend instance. If provided, this backend is used instead of creating one from config. Enables custom search backends (e.g. vector DB, Elasticsearch) without modifying the built-in factory. |
None
|
publishers
|
Optional[list[AssetPublisher]]
|
Optional list of :class: |
None
|
skills
|
Optional[list[Skill]]
|
Optional list of :class: |
None
|
states
|
Optional[list[State]]
|
Optional list of :class: |
None
|
catalog
|
Optional[CatalogIntegration]
|
Optional policy-aware data catalog integration. When omitted, catalog lifecycle, tools, and session behavior remain unchanged. |
None
|
Source code in src/agora_workbench/code_execution/server.py
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 | |
warm()
async
¶
Pre-initialize the execution environment without serving requests.
Call this during Docker builds or process startup to avoid cold-start latency. It prepares the Python environment, provisions assets according to the ServerConfig (e.g. when auto_provision is enabled), and registers the execution kernel.
Source code in src/agora_workbench/code_execution/server.py
main(*, default_host='127.0.0.1', default_port=8000)
¶
CLI entrypoint that handles --warm, --host, and --port.
Call this from your server's if __name__ == "__main__" block to get
standard flag handling without manual sys.argv parsing::
server = MyDomainServer(...)
server.main()
Flags
--warm Pre-initialize the environment and exit (no HTTP server). --host HOST Bind address (default: default_host, or HOST env var). --port PORT Bind port (default: default_port, or PORT env var). --allow-unauthenticated-remote Permit open/no-op auth on a non-loopback bind. --no-allow-unauthenticated-remote Disable an acknowledgement inherited from the environment.
Source code in src/agora_workbench/code_execution/server.py
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 | |
get_tool_name()
¶
public_url()
¶
Best-effort base URL for outbound references (e.g. download links).
Falls back to http://localhost:<port> when called before
:meth:run_http has stashed the bind host/port. Callers that need
the real externally-visible URL (containerized deployments behind a
proxy / Ingress) should set SERVER_PUBLIC_URL instead — that env
var takes precedence wherever this method is consulted.
Source code in src/agora_workbench/code_execution/server.py
preprocess_code(code)
¶
Preprocess code before execution.
Override to add imports, setup code, or modify the input.
validate_code(code)
¶
Validate user-provided code before execution.
This is a thin instance-method wrapper around the default implementation
in execution_defaults.validate_code, which expects self as its first
argument.
Override to adjust behavior.
Source code in src/agora_workbench/code_execution/server.py
postprocess_result(result)
¶
Postprocess execution result.
Override to filter output, add metadata, or modify the result.
get_data_lake_capabilities(session)
async
¶
Return caller-effective catalog capabilities for application adapters.
Source code in src/agora_workbench/code_execution/server.py
get_python_executable()
async
¶
Return the path to the Python interpreter for this environment.
This will automatically build the environment if needed and build_environment=True.