@@ -510,6 +510,133 @@ enable_plugin(info.name)
510510uninstall_plugin(info.name)
511511```
512512
513+ ## Multiple Marketplace Registrations
514+
515+ For enterprise and team scenarios, you can register multiple plugin marketplaces
516+ with different loading strategies. This allows you to:
517+
518+ - Register internal team marketplaces alongside the public marketplace
519+ - Control which plugins auto-load at conversation start vs load on-demand
520+ - Reference plugins from specific marketplaces using the ` plugin@marketplace ` syntax
521+
522+ ### Loading Strategies
523+
524+ | Strategy | Behavior |
525+ | ----------| ----------|
526+ | ` auto_load="all" ` | All plugins from the marketplace load automatically when a conversation starts |
527+ | ` auto_load=None ` (default) | Marketplace is registered but plugins are loaded on-demand via ` load_plugin() ` |
528+
529+ ### Plugin Reference Syntax
530+
531+ Use the ` plugin-name@marketplace-name ` format to explicitly specify which
532+ marketplace a plugin comes from. This syntax follows the same convention as
533+ [ Claude Code's plugin install command] ( https://code.claude.com/docs/en/plugins-reference ) .
534+
535+ ``` python icon="python"
536+ # Load a specific plugin from a registered marketplace
537+ conversation.load_plugin(" greeter@demo" )
538+
539+ # If only one marketplace has the plugin, the marketplace name is optional
540+ conversation.load_plugin(" greeter" )
541+ ```
542+
543+ ### Example: Auto-load and On-demand Loading
544+
545+ The example below demonstrates registering two marketplaces with different
546+ loading strategies, then loading an additional plugin on-demand.
547+
548+ <Note >
549+ Source: [ examples/05_skills_and_plugins/04_multiple_marketplace_registrations/main.py] ( https://github.com/OpenHands/software-agent-sdk/blob/main/examples/05_skills_and_plugins/04_multiple_marketplace_registrations/main.py )
550+ </Note >
551+
552+ ``` python icon="python" expandable examples/05_skills_and_plugins/04_multiple_marketplace_registrations/main.py
553+ """ Example: Multiple Marketplace Registrations
554+
555+ Demonstrates two loading strategies for marketplace plugins:
556+
557+ - auto_load="all": Plugins loaded automatically at conversation start
558+ - auto_load=None: Plugins loaded on-demand via conversation.load_plugin()
559+
560+ This example uses pre-created marketplaces in:
561+ - ./auto_marketplace/ - auto-loaded at conversation start
562+ - ./demo_marketplace/ - loaded on-demand
563+ """
564+
565+ import os
566+ from pathlib import Path
567+
568+ from openhands.sdk import LLM , Agent, AgentContext, Conversation
569+ from openhands.sdk.plugin import MarketplaceRegistration
570+
571+ SCRIPT_DIR = Path(__file__ ).parent
572+
573+
574+ def main ():
575+ llm = LLM(
576+ model = os.getenv(" LLM_MODEL" , " anthropic/claude-sonnet-4-5-20250929" ),
577+ api_key = os.getenv(" LLM_API_KEY" ),
578+ base_url = os.getenv(" LLM_BASE_URL" ),
579+ )
580+
581+ # Register two marketplaces with different loading strategies
582+ agent_context = AgentContext(
583+ registered_marketplaces = [
584+ # Auto-loaded: plugins available immediately when conversation starts
585+ MarketplaceRegistration(
586+ name = " auto" ,
587+ source = str (SCRIPT_DIR / " auto_marketplace" ),
588+ auto_load = " all" ,
589+ ),
590+ # On-demand: registered but not loaded until explicitly requested
591+ MarketplaceRegistration(
592+ name = " demo" ,
593+ source = str (SCRIPT_DIR / " demo_marketplace" ),
594+ # auto_load=None (default) - use load_plugin() to load
595+ ),
596+ ],
597+ )
598+
599+ agent = Agent(llm = llm, tools = [], agent_context = agent_context)
600+ conversation = Conversation(agent = agent, workspace = os.getcwd())
601+
602+ # The "auto" marketplace plugins are already loaded
603+ # Now load an additional plugin on-demand from "demo" marketplace
604+ # Format: "plugin-name@marketplace-name" (same as Claude Code plugin syntax)
605+ conversation.load_plugin(" greeter@demo" )
606+
607+ resolved = conversation.resolved_plugins
608+ if resolved:
609+ print (f " Loaded { len (resolved)} plugin(s): " )
610+ for plugin in resolved:
611+ print (f " - { plugin.source} " )
612+
613+ # Use skills from both plugins
614+ conversation.send_message(" Give me a tip, then greet me!" )
615+ conversation.run()
616+
617+ print (f " \n EXAMPLE_COST: { llm.metrics.accumulated_cost:.4f } " )
618+
619+
620+ if __name__ == " __main__" :
621+ if not os.getenv(" LLM_API_KEY" ):
622+ print (" Set LLM_API_KEY to run this example" )
623+ print (" EXAMPLE_COST: 0" )
624+ else :
625+ main()
626+ ```
627+
628+ <RunExampleCode path_to_script = " examples/05_skills_and_plugins/04_multiple_marketplace_registrations/main.py" />
629+
630+ ### MarketplaceRegistration Fields
631+
632+ | Field | Type | Description |
633+ | -------| ------| -------------|
634+ | ` name ` | ` str ` | Identifier for this marketplace registration |
635+ | ` source ` | ` str ` | Plugin source: ` github:owner/repo ` , git URL, or local path |
636+ | ` ref ` | ` str \| None ` | Optional branch, tag, or commit for the marketplace repo |
637+ | ` repo_path ` | ` str \| None ` | Subdirectory within repo (for monorepos) |
638+ | ` auto_load ` | ` "all" \| None ` | Loading strategy (default: ` None ` ) |
639+
513640## Next Steps
514641
515642- ** [ Skills] ( /sdk/guides/skill ) ** - Learn more about skills and triggers
0 commit comments