Skip to content

System Bundles & Atoms

JarvisCore's integration model is built around two related ideas: atoms and system bundles.

An atom is a single, versioned, self-contained Python function that performs one action against one external system — send a Slack message, create a GitHub issue, read a Google Sheet row. It has a fixed signature, it owns its HTTP transport, and it never holds state.

A system bundle is a generated Python class that groups all atoms for a given system together — SlackCapabilities, GitHubCapabilities, StripeCapabilities — making them available to agent code and the code sandbox as a typed, injectable unit.

[!NOTE] Coming from MCP? System bundles are JarvisCore's native alternative to MCP tool servers. They are simpler (plain Python functions, no server process), tighter to the agent runtime, and carry an execution history that drives automatic promotion to production-ready status. See the MCP comparison below.


The Atom

Every atom is a standalone Python function that follows a strict contract:

jarviscore/integrations/atoms/slack/slack_send_message.py
def slack_send_message(auth_info: dict, channel: str, text: str, thread_ts: str = None) -> dict:
    import requests
    _base = "https://slack.com/api"
    _h = {"Authorization": f"Bearer {auth_info.get('access_token', '')}", "Content-Type": "application/json"}

    def _post(p, data=None):
        r = requests.post(f"{_base}{p}", headers=_h, json=data, timeout=30)
        r.raise_for_status()
        return r.json()

    payload = {"channel": channel, "text": text}
    if thread_ts:
        payload["thread_ts"] = thread_ts

    resp = _post("/chat.postMessage", data=payload)
    if not resp.get("ok"):
        raise RuntimeError(resp.get("error", "Slack API error"))

    return {"ts": resp["ts"], "channel": resp["channel"], "text": text}

The atom contract:

Property Rule
First parameter Always auth_info: dict — populated by Nexus at call time
Return type Always dict — structured, never raw HTTP response
Transport Self-contained — imports requests internally
State Stateless — no class, no instance, no side effects beyond the API call
Error handling Raises on failure — the sandbox catches and reports

Atoms intentionally embed their own import requests rather than relying on a shared HTTP client. This makes them independently executable in the code sandbox and portable across contexts.


The System Bundle

A system bundle is a generated {System}Capabilities class assembled from all atoms registered for a given system. The FunctionRegistry.create_system_bundle() method produces it:

# Generated by FunctionRegistry.create_system_bundle("slack")
class SlackCapabilities:
    """Capabilities for the Slack system.

    Available functions:
      - slack_send_message(auth_info, channel, text, thread_ts)
      - slack_list_channels(auth_info)
      - slack_get_channel_history(auth_info, channel_id, limit)
      - slack_create_channel(auth_info, name, is_private)
      - slack_invite_user(auth_info, channel_id, user_id)
      - slack_add_reaction(auth_info, channel, timestamp, name)
    """

    @staticmethod
    def slack_send_message(auth_info, channel, text, thread_ts=None):
        # ... atom code injected here

The bundle class is then prepended to any code the CoderSubAgent executes in the sandbox. This means agent-generated code can reference SlackCapabilities.slack_send_message(...) directly, with the atom implementation guaranteed to be available.


The Function Registry

Atoms are stored and tracked in the FunctionRegistry. The registry is a local filesystem store with an optional Redis cognitive projection for cross-agent discovery.

logs/function_registry/
├── metadata/
│   ├── slack_send_message.json      ← execution stats, stage, SHA256
│   └── github_create_issue.json
├── atoms/
│   ├── slack/
│   │   ├── slack_send_message_v1.py
│   │   └── slack_send_message_v2.py   ← atoms are immutable and versioned
│   └── github/
│       └── github_create_issue_v1.py
└── bundles/
    ├── slack_bundle.py
    └── github_bundle.py

Every atom is immutable and versioned. When an atom is updated (by CoderSubAgent or by seeding), a new _v2.py is written rather than overwriting _v1.py. The SHA-256 hash of the function source is stored in metadata and verified on load.

Graduation

Every atom has a stage:

Stage Threshold Meaning
candidate Newly registered Generated or seeded but not yet executed in production
verified 1 successful execution Has run successfully at least once
golden 5 successful executions Production-tested; preferred by CoderSubAgent on registry lookups

Stage advances automatically when update_execution_stats(success=True) is called after a successful sandbox run. When CoderSubAgent searches the registry for an existing function, golden atoms score higher than verified, which score higher than candidates.


JIT: Just-In-Time Compilation

The registry's most powerful capability is JIT — agent code generation for systems and actions that don't yet have a pre-built atom.

When a task requires calling an external system and no matching atom exists in the registry, CoderSubAgent follows this fallback ladder:

1. check_registry(task)
   └── Found a golden/verified atom? → reuse it, skip to execute

2. write_code(task)
   └── CoderSubAgent writes a new atom from its training knowledge
   └── ValidationLayer checks syntax and structure
   └── Atom stored as "candidate" in registry

3. execute_code(function_name)
   └── SandboxExecutor runs the atom in an isolated environment
   └── On success → stage promoted to "verified"
   └── On failure → diagnose, rewrite, retry (max 2 attempts)
   └── On persistent failure → delegate_research as last resort

4. register_success(function_name)
   └── Execution stats updated
   └── Atom available for reuse in future calls

This means the first time your agent needs to call, say, quickbooks_create_invoice, it writes and tests that function live. Every subsequent call skips straight to step 1 — the atom is already in the registry.

Naming convention: All atoms registered by CoderSubAgent follow {system}_{action} — e.g. stripe_create_payment_intent, notion_create_page, github_list_open_prs.


The 19 Pre-Seeded Systems

JarvisCore ships with 77 pre-built atoms across 19 system bundles, seeded at startup via seed_registry.py. Pre-seeded atoms start as candidate and promote to verified after first successful use in your environment.

Communication

System Atoms Auth
Slack send_message, list_channels, get_history, create_channel, invite_user, add_reaction OAuth2
Gmail send_email, list_messages, get_message, create_draft OAuth2
SendGrid send_email, get_stats API key
Brevo send_email, get_contacts, create_contact API key
Mailchimp add_subscriber, get_lists, send_campaign API key

Development

System Atoms Auth
GitHub create_issue, list_issues, create_pr, merge_pr, get_file, update_file, add_comment, list_repos OAuth2
Linear create_issue, get_issue, list_issues, update_issue, search_issues OAuth2
Jira create_issue, get_issue, update_issue Basic auth

Productivity

System Atoms Auth
Notion create_page, get_page, list_databases, query_database, update_page, search OAuth2
Google Drive list_files, upload_file, download_file, share_file OAuth2
Google Sheets read_range, write_range, append_rows, get_spreadsheet OAuth2
Google Calendar list_events, create_event, delete_event OAuth2
Airtable list_records, create_record, update_record, delete_record, get_record, search_records, get_schema, batch_create API key

CRM & Sales

System Atoms Auth
HubSpot create_contact, get_contact, create_deal, update_deal, list_contacts OAuth2
Salesforce create_record, query_records, update_record, get_record OAuth2
Apollo search_people, enrich_person, get_account API key

Finance

System Atoms Auth
Stripe create_payment_intent, list_customers, create_customer, get_balance API key
QuickBooks get_company_info, list_invoices, create_invoice, get_reports OAuth2
System Atoms Auth
Serper search, news_search API key

This Is Not MCP

The Model Context Protocol (MCP) by Anthropic is a standard for connecting LLM applications to external tools via a client-server protocol. It's a well-designed standard — MCP servers run as separate processes, expose tools via a JSON-RPC-like protocol, and clients discover and call them at runtime.

JarvisCore's atom model takes a different approach, and it's worth being explicit about the trade-offs:

MCP JarvisCore Atoms
Architecture Client + server processes Plain Python functions, no server
Discovery MCP client discovers tools at runtime from the server Registry lookup; JIT generation if missing
Transport JSON-RPC over stdio/HTTP Direct Python call in sandbox
Versioning Server manages versions Immutable _v1, _v2 files + SHA256
Execution history Not tracked Tracked — drives candidate→verified→golden promotion
New tools Write a new MCP server handler CoderSubAgent generates the atom JIT from a task description
Auth Varies by implementation Always via Nexus — credentials never in agent code

Does JarvisCore support MCP tools? Not natively — there is no built-in MCP client. However, CustomAgent is flexible enough to wrap an MCP client directly:

class MCPAgent(CustomAgent):
    """Agent that wraps an existing MCP tool server."""
    role = "mcp_bridge"
    capabilities = ["mcp_tools"]

    async def setup(self):
        await super().setup()
        from mcp import Client
        self.mcp = Client("stdio://./my-server.py")
        await self.mcp.connect()

    async def run(self, message):
        result = await self.mcp.call_tool("my_tool", message.data)
        return result

This pattern lets teams adopt JarvisCore's orchestration, memory, and P2P mesh while keeping existing MCP tool servers intact. The atom model and MCP are not mutually exclusive — they operate at different layers.

The pragmatic question for teams evaluating JarvisCore: if you have MCP servers already built and working, wrap them in a CustomAgent. If you are starting fresh, atoms give you versioning, execution history, and JIT generation out of the box.


Further Reading