Skip to content

perf: lazy-load MCP SDK and event types to reduce cold start by ~29%#5584

Merged
joaomdmoura merged 10 commits intomainfrom
perf/lazy-load-imports
Apr 22, 2026
Merged

perf: lazy-load MCP SDK and event types to reduce cold start by ~29%#5584
joaomdmoura merged 10 commits intomainfrom
perf/lazy-load-imports

Conversation

@iris-clawd
Copy link
Copy Markdown
Contributor

@iris-clawd iris-clawd commented Apr 22, 2026

Summary

Reduces import crewai cold start time by lazy-loading heavy dependencies that aren't needed at import time.

Baseline: ~4.7s → After: ~3.4s (warm cache, ~29% improvement)

Changes

1. Fix MCP import path in agent/core.py

  • Changed from crewai.mcp import MCPServerConfigfrom crewai.mcp.config import MCPServerConfig
  • This avoids triggering mcp/__init__.py which eagerly loaded the full mcp SDK (~400ms)
  • Moved MCPToolResolver import into get_mcp_tools() method body (only needed at runtime)

2. Lazy-load heavy MCP imports in mcp/__init__.py

  • MCPClient, MCPToolResolver, BaseTransport, TransportType now use __getattr__ lazy loading
  • Lightweight config/filter types remain eagerly imported
  • Follows the same pattern already used in crewai/__init__.py for Memory

3. Lazy-load all event type modules in events/__init__.py

  • Previously only agent_events were lazy-loaded; now all 12 event type modules are lazy
  • events/__init__.py runs whenever ANY crewai.events.* submodule is accessed (Python package init), so this prevents loading ~12 Pydantic model modules unnecessarily
  • TYPE_CHECKING imports preserved for IDE/type-checker support
  • __all__ unchanged — public API is identical

What's NOT changed (intentionally)

  • Flow — too deeply entangled (model_rebuild, Entity union type)
  • Knowledge/chromadb — needs storage-layer refactoring (separate PR)
  • A2A config — needed by model_rebuild for Pydantic field resolution

Testing

  • python -c 'import crewai' — ✅ works
  • from crewai.events import CrewKickoffStartedEvent, LLMCallStartedEvent, ... — ✅ lazy resolution works
  • from crewai.events import crewai_event_bus, BaseEventListener — ✅ core imports unchanged
  • All existing __all__ exports preserved

Note

Medium Risk
Moderate risk because it changes import-time behavior via __getattr__ lazy loading in public packages, which can surface subtle runtime import/order issues; also updates dependency pins (notably lxml) that could affect optional integrations.

Overview
Improves import crewai performance by avoiding heavy imports at package init: crewai.mcp now lazy-loads MCPClient/MCPToolResolver/transport types, crewai.events lazy-loads all event type classes (not just agent events), and Agent.get_mcp_tools() defers importing MCPToolResolver while switching MCPServerConfig to import from crewai.mcp.config.

Adds a new GitHub Actions workflow (import-time.yml) that benchmarks import crewai on the PR vs base branch and fails if median import time regresses by more than 5%. Also updates tooling/deps: bumps lxml in crewai-tools optional rag extras for a security advisory, adjusts [tool.uv].exclude-newer, and ignores UNKNOWN.egg-info/ in .gitignore.

Reviewed by Cursor Bugbot for commit 634ff3d. Bugbot is set up for automated code reviews on this repo. Configure here.

- Change 'from crewai.mcp import MCPServerConfig' to direct path
  'from crewai.mcp.config import MCPServerConfig' to avoid triggering
  mcp/__init__.py which eagerly loads the full mcp SDK (~300-400ms)
- Move MCPToolResolver import into get_mcp_tools() method body since
  it's only used at runtime, not in type annotations

Saves ~200ms on 'import crewai' cold start.
MCPClient, MCPToolResolver, BaseTransport, and TransportType now use
__getattr__ lazy loading. These pull in the full mcp SDK (~400ms) but
are only needed at runtime when agents actually connect to MCP servers.

Lightweight config and filter types remain eagerly imported.
Previously only agent_events were lazy-loaded; all other event type
modules (crew, flow, knowledge, llm, guardrail, logging, mcp, memory,
reasoning, skill, task, tool_usage) were eagerly imported at package
init time. Since events/__init__.py runs whenever ANY crewai.events.*
submodule is accessed, this loaded ~12 Pydantic model modules
unnecessarily.

Now all event types use the same __getattr__ lazy-loading pattern,
with TYPE_CHECKING imports preserved for IDE/type-checker support.

Saves ~550ms on 'import crewai' cold start.
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 24f774d. Configure here.

Comment thread UNKNOWN.egg-info/PKG-INFO Outdated
@github-actions github-actions Bot added size/L and removed size/XL labels Apr 22, 2026
Fixes F821 (ruff) and name-defined (mypy) from lazy-loading the
MCP import. The type annotation on _mcp_resolver needs the name
available at type-check time.
lxml 5.3.2 has a known vulnerability. Bump to 5.4.0+ which
includes the fix (libxml2 2.13.8). The previous <5.4.0 pin
was for etree import issues that have since been resolved.
@alex-clawd alex-clawd force-pushed the perf/lazy-load-imports branch from a79c6d9 to bbecebd Compare April 22, 2026 04:44
lxml 6.1.0 (GHSA fix) was released April 17 but the exclude-newer
date was set to April 17, missing it by timestamp. Bump to April 22.
scripts/benchmark_import_time.py measures import crewai cold start
in fresh subprocesses. Supports --runs, --json (for CI), and
--threshold (fail if median exceeds N seconds).

The companion GitHub Action workflow needs to be pushed separately
(requires workflow scope).
@github-actions github-actions Bot added size/XL and removed size/L labels Apr 22, 2026
Comment thread .github/workflows/import-time.yml Fixed
…ntain permissions'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
@joaomdmoura joaomdmoura merged commit 3be2fb6 into main Apr 22, 2026
55 checks passed
@joaomdmoura joaomdmoura deleted the perf/lazy-load-imports branch April 22, 2026 05:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants