Compare commits
16 Commits
main
...
docs/claud
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6494de5ad | ||
|
|
c5421c61f5 | ||
|
|
2ccd4b99d0 | ||
|
|
5f0871707e | ||
|
|
b80891b312 | ||
|
|
149c315dfa | ||
|
|
b26d7ee9b2 | ||
|
|
1dd9cfaaf3 | ||
|
|
28600d7956 | ||
|
|
36ab08129c | ||
|
|
ec78c7637b | ||
|
|
aa710d9310 | ||
|
|
050cd70060 | ||
|
|
1ccd92e0c8 | ||
|
|
403725b970 | ||
|
|
03a222d9b5 |
@ -1,29 +1,100 @@
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
// Three quick-start lanes — keeps the home page from being a wall of text
|
||||||
|
// and lets builders, operators, and integrators each find their entry
|
||||||
|
// point in one click.
|
||||||
|
const lanes = [
|
||||||
|
{
|
||||||
|
kicker: '01',
|
||||||
|
title: 'Build a workspace',
|
||||||
|
body: 'Pick a runtime template (Claude Code, LangGraph, CrewAI, Hermes, …), wire your tools, and ship.',
|
||||||
|
href: '/docs/workspace',
|
||||||
|
cta: 'Workspace guide →',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kicker: '02',
|
||||||
|
title: 'Run an organisation',
|
||||||
|
body: 'Topology, A2A, three-tier memory, governance — the platform layer that ties multi-agent teams together.',
|
||||||
|
href: '/docs/platform',
|
||||||
|
cta: 'Platform reference →',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kicker: '03',
|
||||||
|
title: 'Publish to the Marketplace',
|
||||||
|
body: 'Plugins, agents, and org bundles ship as signed manifests. Authors keep 80%, paid via Stripe Connect.',
|
||||||
|
href: '/docs/marketplace',
|
||||||
|
cta: 'Author guide →',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
return (
|
return (
|
||||||
<main className="flex flex-1 flex-col items-center justify-center px-6 py-24 text-center">
|
<main className="flex flex-1 flex-col">
|
||||||
<h1 className="mb-4 text-5xl font-bold tracking-tight sm:text-6xl">
|
{/* Statusbar — mirrors the landing's "All systems · status.* · phase" strip */}
|
||||||
Molecule AI
|
<div className="border-b border-fd-border bg-fd-muted px-6 py-1.5 text-[11px] font-mono text-fd-muted-foreground flex flex-wrap justify-between gap-4">
|
||||||
|
<span>
|
||||||
|
<span className="inline-block size-1.5 rounded-full bg-[#2f7a4d] align-middle mr-1.5" />
|
||||||
|
All systems · status.moleculesai.app
|
||||||
|
</span>
|
||||||
|
<span>Phase 33 shipped · Phase 35 Marketplace public beta</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Hero */}
|
||||||
|
<section className="px-6 py-20 sm:py-28 max-w-6xl mx-auto w-full">
|
||||||
|
<div className="text-[11px] font-mono uppercase tracking-[0.08em] text-fd-muted-foreground mb-4 flex items-center gap-2">
|
||||||
|
<span className="inline-block size-1.5 rounded-full bg-[#c0532b]" />
|
||||||
|
Documentation
|
||||||
|
</div>
|
||||||
|
<h1 className="text-5xl sm:text-6xl font-semibold tracking-tight leading-[1.05] mb-5 max-w-3xl">
|
||||||
|
The operating system for{' '}
|
||||||
|
<span className="text-[#3b5bdb]">AI agent organizations.</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p className="mb-8 max-w-2xl text-lg text-fd-muted-foreground">
|
<p className="text-lg text-fd-muted-foreground max-w-2xl leading-relaxed mb-8">
|
||||||
Build and run multi-agent organisations. Templates, plugins, channels,
|
Build and run multi-agent organisations the way you'd staff a company.
|
||||||
and the runtime that ties them together — documented end to end.
|
Templates, plugins, channels, runtimes, governance — documented end
|
||||||
|
to end.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-wrap items-center justify-center gap-3">
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
<Link
|
<Link
|
||||||
href="/docs"
|
href="/docs"
|
||||||
className="rounded-md bg-fd-primary px-5 py-2.5 text-sm font-medium text-fd-primary-foreground transition-colors hover:opacity-90"
|
className="rounded-md bg-fd-primary px-5 py-2.5 text-sm font-medium text-fd-primary-foreground transition hover:opacity-90"
|
||||||
>
|
>
|
||||||
Read the docs
|
Read the docs
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="https://github.com/Molecule-AI/molecule-monorepo"
|
href="https://github.com/Molecule-AI"
|
||||||
className="rounded-md border border-fd-border px-5 py-2.5 text-sm font-medium transition-colors hover:bg-fd-muted"
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="rounded-md border border-fd-border px-5 py-2.5 text-sm font-medium transition hover:bg-fd-muted"
|
||||||
>
|
>
|
||||||
View on GitHub
|
View on GitHub
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Three lanes */}
|
||||||
|
<section className="px-6 pb-24 max-w-6xl mx-auto w-full">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
|
{lanes.map((lane) => (
|
||||||
|
<Link
|
||||||
|
key={lane.kicker}
|
||||||
|
href={lane.href}
|
||||||
|
className="group rounded-lg border border-fd-border bg-fd-card p-6 transition hover:border-fd-foreground hover:-translate-y-0.5"
|
||||||
|
>
|
||||||
|
<div className="text-[11px] font-mono text-[#3b5bdb] mb-3 tracking-[0.08em]">
|
||||||
|
{lane.kicker}
|
||||||
|
</div>
|
||||||
|
<h3 className="text-base font-semibold mb-2">{lane.title}</h3>
|
||||||
|
<p className="text-sm text-fd-muted-foreground leading-relaxed mb-4">
|
||||||
|
{lane.body}
|
||||||
|
</p>
|
||||||
|
<div className="text-xs font-mono text-fd-foreground group-hover:text-[#3b5bdb] transition">
|
||||||
|
{lane.cta}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,32 @@
|
|||||||
@import 'tailwindcss';
|
@import 'tailwindcss';
|
||||||
@import 'fumadocs-ui/css/neutral.css';
|
@import 'fumadocs-ui/css/neutral.css';
|
||||||
@import 'fumadocs-ui/css/preset.css';
|
@import 'fumadocs-ui/css/preset.css';
|
||||||
|
|
||||||
|
/* Warm-paper light theme — aligned with the landing page (moleculesai.app).
|
||||||
|
Tokens map fumadocs' @theme variables onto our brand palette so docs,
|
||||||
|
marketing, and the canvas read as one product. */
|
||||||
|
@theme {
|
||||||
|
--font-sans: var(--font-geist), ui-sans-serif, system-ui, sans-serif;
|
||||||
|
--font-mono: var(--font-mono), ui-monospace, SFMono-Regular, monospace;
|
||||||
|
|
||||||
|
--color-fd-background: #fafaf7;
|
||||||
|
--color-fd-foreground: #15181c;
|
||||||
|
--color-fd-muted: #f3f1ec;
|
||||||
|
--color-fd-muted-foreground: #5a5e66;
|
||||||
|
--color-fd-popover: #ffffff;
|
||||||
|
--color-fd-popover-foreground: #15181c;
|
||||||
|
--color-fd-card: #ffffff;
|
||||||
|
--color-fd-card-foreground: #15181c;
|
||||||
|
--color-fd-border: #e6e2d8;
|
||||||
|
--color-fd-primary: #3b5bdb;
|
||||||
|
--color-fd-primary-foreground: #ffffff;
|
||||||
|
--color-fd-secondary: #efece4;
|
||||||
|
--color-fd-secondary-foreground: #15181c;
|
||||||
|
--color-fd-accent: #efece4;
|
||||||
|
--color-fd-accent-foreground: #15181c;
|
||||||
|
--color-fd-ring: #3b5bdb;
|
||||||
|
--color-fd-overlay: hsla(0, 0%, 0%, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark mode keeps fumadocs' neutral defaults — readers expect docs sites
|
||||||
|
to honor their system preference, and our landing only ships light. */
|
||||||
|
|||||||
@ -1,7 +1,50 @@
|
|||||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||||
|
|
||||||
|
// Molecule logo — the same triangle-of-nodes mark used on moleculesai.app.
|
||||||
|
// Inlined as a JSX element so fumadocs renders it in the topbar without a
|
||||||
|
// separate asset request.
|
||||||
|
const MoleculeLogo = (
|
||||||
|
<svg
|
||||||
|
width="22"
|
||||||
|
height="22"
|
||||||
|
viewBox="0 0 28 28"
|
||||||
|
fill="none"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<circle cx="14" cy="6" r="2.5" fill="currentColor" />
|
||||||
|
<circle cx="6" cy="20" r="2.5" fill="currentColor" />
|
||||||
|
<circle cx="22" cy="20" r="2.5" fill="currentColor" />
|
||||||
|
<circle
|
||||||
|
cx="14"
|
||||||
|
cy="14"
|
||||||
|
r="1.6"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.2"
|
||||||
|
/>
|
||||||
|
<line x1="14" y1="8.5" x2="14" y2="12.6" stroke="currentColor" strokeWidth="1.2" />
|
||||||
|
<line x1="8" y1="18.5" x2="12.7" y2="14.8" stroke="currentColor" strokeWidth="1.2" />
|
||||||
|
<line x1="20" y1="18.5" x2="15.3" y2="14.8" stroke="currentColor" strokeWidth="1.2" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
export const baseOptions: BaseLayoutProps = {
|
export const baseOptions: BaseLayoutProps = {
|
||||||
nav: {
|
nav: {
|
||||||
title: 'Molecule AI',
|
title: (
|
||||||
|
<span className="flex items-center gap-2 font-semibold tracking-tight">
|
||||||
|
{MoleculeLogo}
|
||||||
|
<span>Molecule AI</span>
|
||||||
|
<span className="text-xs uppercase tracking-[0.08em] text-fd-muted-foreground font-mono">
|
||||||
|
Docs
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
url: 'https://doc.moleculesai.app',
|
||||||
},
|
},
|
||||||
|
links: [
|
||||||
|
{ text: 'Platform', url: 'https://app.moleculesai.app', external: true },
|
||||||
|
{ text: 'Marketplace', url: 'https://market.moleculesai.app', external: true },
|
||||||
|
{ text: 'Landing', url: 'https://www.moleculesai.app', external: true },
|
||||||
|
],
|
||||||
|
githubUrl: 'https://github.com/Molecule-AI',
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,10 +1,16 @@
|
|||||||
import './global.css';
|
import './global.css';
|
||||||
import { RootProvider } from 'fumadocs-ui/provider/next';
|
import { RootProvider } from 'fumadocs-ui/provider/next';
|
||||||
import { Inter } from 'next/font/google';
|
import { Geist, JetBrains_Mono } from 'next/font/google';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
const inter = Inter({
|
const geist = Geist({
|
||||||
subsets: ['latin'],
|
subsets: ['latin'],
|
||||||
|
variable: '--font-geist',
|
||||||
|
});
|
||||||
|
|
||||||
|
const jetbrains = JetBrains_Mono({
|
||||||
|
subsets: ['latin'],
|
||||||
|
variable: '--font-mono',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
@ -19,8 +25,12 @@ export const metadata = {
|
|||||||
|
|
||||||
export default function Layout({ children }: { children: ReactNode }) {
|
export default function Layout({ children }: { children: ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" className={inter.className} suppressHydrationWarning>
|
<html
|
||||||
<body className="flex flex-col min-h-screen">
|
lang="en"
|
||||||
|
className={`${geist.variable} ${jetbrains.variable}`}
|
||||||
|
suppressHydrationWarning
|
||||||
|
>
|
||||||
|
<body className="flex flex-col min-h-screen font-sans">
|
||||||
<RootProvider>{children}</RootProvider>
|
<RootProvider>{children}</RootProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -73,6 +73,72 @@ At a high level, `workspace/main.py` does this:
|
|||||||
10. Start the skill watcher when skills are configured.
|
10. Start the skill watcher when skills are configured.
|
||||||
11. Serve the A2A app through Uvicorn.
|
11. Serve the A2A app through Uvicorn.
|
||||||
|
|
||||||
|
## Boot-Smoke Contract (`MOLECULE_SMOKE_MODE`)
|
||||||
|
|
||||||
|
The image-publish CI pipeline runs each template's image with `MOLECULE_SMOKE_MODE=1` to exercise lazy imports inside `executor.execute()` against stub credentials and no network. The runtime detects the env var, invokes `executor.execute()` once with a stubbed `RequestContext` and a short timeout, then exits — registration, heartbeats, and the A2A server are skipped.
|
||||||
|
|
||||||
|
This catches lazy imports that pure `python3 -c "import adapter"` smokes miss: imports nested inside `if`-branches, deferred until first call, or behind `importlib.import_module()`.
|
||||||
|
|
||||||
|
### What adapter authors need to do
|
||||||
|
|
||||||
|
**Most adapters need to do nothing.** If `setup()` only writes files, parses config, or instantiates Python objects, the smoke gate just works.
|
||||||
|
|
||||||
|
**Adapters whose `setup()` does real I/O must opt out of that I/O under smoke mode.** This applies to:
|
||||||
|
|
||||||
|
- spawning subprocesses that require valid credentials (e.g. a gateway daemon)
|
||||||
|
- making real network calls
|
||||||
|
- writing to filesystem locations that need a specific uid/gid the smoke harness can't guarantee
|
||||||
|
|
||||||
|
The contract:
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def setup(self, config: AdapterConfig) -> None:
|
||||||
|
if os.environ.get("MOLECULE_SMOKE_MODE") == "1":
|
||||||
|
return # skip real I/O; runtime's smoke short-circuit handles the rest
|
||||||
|
# ... real setup ...
|
||||||
|
```
|
||||||
|
|
||||||
|
For shell entrypoints that wrap `molecule-runtime`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
if [ "${MOLECULE_SMOKE_MODE:-0}" = "1" ]; then
|
||||||
|
exec molecule-runtime
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
### What gets exercised under smoke mode
|
||||||
|
|
||||||
|
- All `/app/*.py` modules import cleanly (covered by a separate static-import smoke step)
|
||||||
|
- `adapter.setup()` runs (with the opt-out above for I/O-heavy adapters)
|
||||||
|
- `adapter.create_executor()` runs
|
||||||
|
- `executor.execute()` is invoked once against a stub `RequestContext`/`EventQueue` with `MOLECULE_SMOKE_TIMEOUT_SECS` (default 5s); a clean timeout exits 0, an import error exits non-zero
|
||||||
|
|
||||||
|
### What the gate does NOT prove
|
||||||
|
|
||||||
|
A green gate means **"imports are healthy enough that `executor.execute()` reaches its body"** — that's the regression class the gate exists to catch (lazy `from x import y` inside an `if`-branch, or `importlib.import_module()` on a path that breaks after a wheel bump).
|
||||||
|
|
||||||
|
It does **not** prove that `execute()` produces the right output for real input. The harness reports PASS in three distinct cases:
|
||||||
|
|
||||||
|
1. **Clean return** — execute() ran to completion within the timeout.
|
||||||
|
2. **Timeout** — execute() was still running when the timer fired (typical for adapters that do real I/O inside execute(): subprocess to a gateway, httpx call to an upstream LLM).
|
||||||
|
3. **Any non-import exception** — execute() raised `RuntimeError`, auth errors, validation errors, etc. The harness only fails on `ImportError`/`ModuleNotFoundError`.
|
||||||
|
|
||||||
|
The stub `RequestContext` carries a non-empty `"smoke test"` text message (so adapters relying on `extract_message_text(ctx)` returning input still work), and the harness never drains the `EventQueue` — what `execute()` writes back is ignored.
|
||||||
|
|
||||||
|
If you need correctness coverage, write a separate integration test that runs the workspace against real or mocked infrastructure — the smoke gate is a strict subset.
|
||||||
|
|
||||||
|
### Stub env the smoke harness sets
|
||||||
|
|
||||||
|
| Var | Value |
|
||||||
|
|---|---|
|
||||||
|
| `MOLECULE_SMOKE_MODE` | `1` |
|
||||||
|
| `MOLECULE_SMOKE_TIMEOUT_SECS` | `10` (CI default) |
|
||||||
|
| `WORKSPACE_ID` | `fake-smoke` |
|
||||||
|
| `PYTHONPATH` | `/app` (mirrors the platform provisioner) |
|
||||||
|
| `CLAUDE_CODE_OAUTH_TOKEN`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, `OPENAI_API_KEY` | `sk-fake-smoke-*` |
|
||||||
|
|
||||||
|
A `config.yaml` from the template repo's root is mounted at `/configs/config.yaml`.
|
||||||
|
|
||||||
## Core Runtime Pieces
|
## Core Runtime Pieces
|
||||||
|
|
||||||
| File | Responsibility |
|
| File | Responsibility |
|
||||||
|
|||||||
@ -21,6 +21,17 @@ register and heartbeat by hand. Use it when your agent can't run an MCP
|
|||||||
stdio server.
|
stdio server.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
|
## Pick the right path
|
||||||
|
|
||||||
|
| Your agent runs as | Best path | Why |
|
||||||
|
|---|---|---|
|
||||||
|
| **An MCP-aware runtime** (Claude Code, Hermes, OpenCode, Cursor, Cline) | [Bring Your Own Runtime (MCP)](/docs/runtime-mcp) | Universal `molecule-mcp` wheel — no HTTP server, no tunnel. |
|
||||||
|
| **A Claude Code session on your laptop** | [Claude Code Channel Plugin](/docs/guides/claude-code-channel-plugin) | Polling-based; no tunnel/public URL needed. Set up in under a minute. |
|
||||||
|
| Any HTTP server with a public URL | The flow on this page (or the [Python SDK guide](/docs/guides/external-agent-registration)) | Push-based; lower latency; works for any A2A-compatible HTTP endpoint. |
|
||||||
|
| A custom A2A server you wrote yourself | The flow on this page | Direct register + heartbeat + handler. |
|
||||||
|
|
||||||
|
The rest of this doc covers the third + fourth rows. For Claude Code or other MCP runtimes, follow the linked guides.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- A running Molecule AI platform (default `http://localhost:8080`)
|
- A running Molecule AI platform (default `http://localhost:8080`)
|
||||||
|
|||||||
222
content/docs/guides/claude-code-channel-plugin.md
Normal file
222
content/docs/guides/claude-code-channel-plugin.md
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
---
|
||||||
|
title: "Claude Code Channel Plugin — Connect a Claude Code Session as an External Workspace"
|
||||||
|
description: "Bridge Molecule A2A traffic into a running Claude Code session via MCP. Polling-based, no tunnel required. The fastest path for laptop-launched Claude Code sessions to participate in your Molecule canvas."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Claude Code Channel Plugin
|
||||||
|
|
||||||
|
Run [Claude Code](https://claude.com/claude-code) on your laptop and have it appear on the Molecule AI canvas as a first-class external workspace. Inbound A2A messages from peer workspaces surface as conversation turns; replies route back through Molecule's A2A endpoints.
|
||||||
|
|
||||||
|
> **What this is:** [`Molecule-AI/molecule-mcp-claude-channel`](https://github.com/Molecule-AI/molecule-mcp-claude-channel) — an MCP-based "channel plugin" that turns a Claude Code session into a Molecule workspace.
|
||||||
|
|
||||||
|
> **What this is NOT:** the [Python SDK / curl register flow](/docs/guides/external-agent-registration) for arbitrary HTTP-speaking agents. That flow needs a public URL the platform can POST to. This one polls — runs on any laptop behind any NAT.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What you get
|
||||||
|
|
||||||
|
```
|
||||||
|
Molecule peer ──A2A──▶ [your workspace] ──poll──▶ [plugin] ──MCP notification──▶ Claude Code
|
||||||
|
▲ │
|
||||||
|
└────── POST /workspaces/:id/a2a ◄── reply_to_workspace ──┘
|
||||||
|
```
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|---|---|
|
||||||
|
| **Inbound latency** | up to `MOLECULE_POLL_INTERVAL_MS` (default 5s) |
|
||||||
|
| **Outbound latency** | direct POST — sub-second |
|
||||||
|
| **Tunnel / public URL** | not required |
|
||||||
|
| **Auth model** | per-workspace bearer token (same as Python SDK) |
|
||||||
|
| **Multi-workspace** | yes, comma-separated list |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
| You need | Notes |
|
||||||
|
|---|---|
|
||||||
|
| A Molecule AI tenant | Self-hosted localhost or your `*.staging.moleculesai.app` SaaS tenant |
|
||||||
|
| One or more workspace IDs | Created via canvas or `POST /workspaces` (see [External Agent Registration](/docs/guides/external-agent-registration)) |
|
||||||
|
| The workspace bearer token | Shown once when the workspace is created — save it from the canvas modal |
|
||||||
|
| Claude Code | `claude` CLI ≥ the version that supports `--channels` |
|
||||||
|
| `bun` | The plugin runs under bun for fast startup; `bun install` is invoked automatically by `start` |
|
||||||
|
|
||||||
|
> **Note:** The platform must be running molecule-core ≥ PR #2300, which shipped the `?since_secs=` query parameter on `GET /workspaces/:id/activity`. Available on all staging-onward and self-hosted main builds after 2026-04-29.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 1 — Create the workspace
|
||||||
|
|
||||||
|
In your Molecule canvas:
|
||||||
|
|
||||||
|
1. Click **+ New workspace**
|
||||||
|
2. Choose **External** runtime
|
||||||
|
3. Set tier as needed; click **Create**
|
||||||
|
4. The "Connect your external agent" modal opens — switch to the **Claude Code** tab
|
||||||
|
5. Copy the entire snippet (everything from the `mkdir -p` line through `claude --channels ...`)
|
||||||
|
|
||||||
|
Or via API:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "$MOLECULE_PLATFORM_URL/workspaces" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"name": "My Claude Code", "external": true, "tier": 2}'
|
||||||
|
```
|
||||||
|
|
||||||
|
The response includes `claude_code_channel_snippet` — same content as the canvas tab, ready to paste.
|
||||||
|
|
||||||
|
## Step 2 — Set up the channel config
|
||||||
|
|
||||||
|
Run the snippet from Step 1. It does two things:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/.claude/channels/molecule
|
||||||
|
cat > ~/.claude/channels/molecule/.env <<'EOF'
|
||||||
|
MOLECULE_PLATFORM_URL=https://your-tenant.staging.moleculesai.app
|
||||||
|
MOLECULE_WORKSPACE_IDS=ws-uuid-1
|
||||||
|
MOLECULE_WORKSPACE_TOKENS=<paste auth_token from create response>
|
||||||
|
EOF
|
||||||
|
chmod 600 ~/.claude/channels/molecule/.env
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace the token placeholder with the workspace bearer from Step 1.
|
||||||
|
|
||||||
|
## Step 3 — Launch Claude Code
|
||||||
|
|
||||||
|
```bash
|
||||||
|
claude --channels plugin:molecule@Molecule-AI/molecule-mcp-claude-channel
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see on stderr (use `--debug` to surface):
|
||||||
|
|
||||||
|
```
|
||||||
|
molecule channel: connected — watching 1 workspace(s) at https://your-tenant.staging.moleculesai.app
|
||||||
|
workspaces: ws-uuid-1
|
||||||
|
poll: every 5000ms with 30s window
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it — the workspace is live on the canvas with a purple **REMOTE** badge, and any A2A traffic the workspace receives surfaces as conversation turns in your Claude Code session.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How replies work
|
||||||
|
|
||||||
|
When a peer's message lands in your session, you'll see a turn with structured metadata:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"method": "notifications/claude/channel",
|
||||||
|
"params": {
|
||||||
|
"content": "Hey, can you take a look at this? <issue body>",
|
||||||
|
"meta": {
|
||||||
|
"source": "molecule",
|
||||||
|
"workspace_id": "ws-uuid-1",
|
||||||
|
"peer_id": "ws-uuid-pm-coordinator",
|
||||||
|
"method": "user_message",
|
||||||
|
"activity_id": "act-...",
|
||||||
|
"ts": "2026-04-29T..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Reply normally — Claude calls the `reply_to_workspace` MCP tool with `peer_id` from the meta block, and the response flows back through `POST /workspaces/:peer_id/a2a` so peers see it just like any other A2A message.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Multi-workspace setup
|
||||||
|
|
||||||
|
Watch multiple workspaces from a single Claude Code session by comma-separating the lists. Both must have the same length and order:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
MOLECULE_WORKSPACE_IDS=ws-pm,ws-researcher,ws-engineer
|
||||||
|
MOLECULE_WORKSPACE_TOKENS=tok-pm,tok-researcher,tok-engineer
|
||||||
|
```
|
||||||
|
|
||||||
|
When Claude replies, the `reply_to_workspace` tool requires `workspace_id` (which of the watched workspaces to reply AS) explicitly. With a single workspace it's implicit.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration reference
|
||||||
|
|
||||||
|
| Variable | Default | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `MOLECULE_PLATFORM_URL` | (required) | Tenant base URL (no trailing slash) |
|
||||||
|
| `MOLECULE_WORKSPACE_IDS` | (required) | Comma-separated workspace UUIDs to watch |
|
||||||
|
| `MOLECULE_WORKSPACE_TOKENS` | (required) | Comma-separated bearer tokens, **same order as IDs** |
|
||||||
|
| `MOLECULE_POLL_INTERVAL_MS` | `5000` | How often each workspace is polled (ms) |
|
||||||
|
| `MOLECULE_POLL_WINDOW_SECS` | `30` | `since_secs` window per poll. Wider than interval to recover from missed ticks |
|
||||||
|
| `MOLECULE_STATE_DIR` | `~/.claude/channels/molecule` | Override state directory (testing) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture notes
|
||||||
|
|
||||||
|
### Why polling instead of push?
|
||||||
|
|
||||||
|
The [Python SDK external-agent flow](/docs/guides/external-agent-registration) uses **push**: register an inbound URL, platform POSTs A2A to that URL. Lower latency but requires a tunnel (ngrok / Cloudflare) or static IP — non-trivial for laptop sessions.
|
||||||
|
|
||||||
|
This plugin uses **polling** as the default because it works through every NAT/firewall with zero infra. Cost: up to `MOLECULE_POLL_INTERVAL_MS` of inbound latency. For production setups where lower latency matters, push mode is on the v0.2 roadmap.
|
||||||
|
|
||||||
|
### Why the 30s window over a 5s interval?
|
||||||
|
|
||||||
|
A single missed tick (transient network blip, GC pause, laptop sleep) shouldn't lose messages. The plugin re-fetches the last 30 seconds on every poll and dedups by `activity_id`, so 25 seconds of overlap is the recovery margin. Increase `MOLECULE_POLL_WINDOW_SECS` for noisier networks.
|
||||||
|
|
||||||
|
### Singleton lock
|
||||||
|
|
||||||
|
Only one channel server runs per host — multiple instances would race the dedup state and double-deliver. The plugin maintains a PID file at `~/.claude/channels/molecule/bot.pid` and on startup kills any stale predecessor. This mirrors the [`@claude-plugins-official/telegram`](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/telegram) pattern.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "molecule channel: required config missing"
|
||||||
|
|
||||||
|
The plugin started before you filled in `.env`. Re-run the snippet from Step 2, then re-launch Claude Code.
|
||||||
|
|
||||||
|
### "molecule channel: poll `<ws-id>` returned 401"
|
||||||
|
|
||||||
|
Bearer token mismatch. Two common causes:
|
||||||
|
|
||||||
|
- The token in `MOLECULE_WORKSPACE_TOKENS` doesn't match the workspace whose ID is in the corresponding position of `MOLECULE_WORKSPACE_IDS`. Verify same-order pairing.
|
||||||
|
- The workspace was rotated and the token was revoked. Generate a new token from the canvas Settings tab (or `POST /admin/workspaces/:id/tokens`).
|
||||||
|
|
||||||
|
### "molecule channel: poll `<ws-id>` returned 404"
|
||||||
|
|
||||||
|
Either the workspace doesn't exist or the `MOLECULE_PLATFORM_URL` is wrong. Confirm:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsS "$MOLECULE_PLATFORM_URL/workspaces/$WS_ID" \
|
||||||
|
-H "Authorization: Bearer $WS_TOKEN" | jq '.workspace.id'
|
||||||
|
```
|
||||||
|
|
||||||
|
### A2A messages aren't surfacing
|
||||||
|
|
||||||
|
Check that the watched workspace is actually receiving them — the plugin only pulls `activity_logs` rows whose `activity_type = a2a_receive`. If peers aren't sending to this workspace, there's nothing to surface. Verify with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsS "$MOLECULE_PLATFORM_URL/workspaces/$WS_ID/activity?type=a2a_receive&limit=10" \
|
||||||
|
-H "Authorization: Bearer $WS_TOKEN" | jq
|
||||||
|
```
|
||||||
|
|
||||||
|
If that returns events but Claude doesn't see them, file an issue at [`Molecule-AI/molecule-mcp-claude-channel`](https://github.com/Molecule-AI/molecule-mcp-claude-channel/issues) with the workspace_id + sample event.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Limitations (v0.1)
|
||||||
|
|
||||||
|
- **Polling-only inbound.** No push mode yet; latency floor is `MOLECULE_POLL_INTERVAL_MS`.
|
||||||
|
- **No pairing flow.** Tokens are configured manually via `.env`; no canvas-side approval handshake.
|
||||||
|
- **No file-attachment download.** URLs surface in the meta block; the host fetches on-demand.
|
||||||
|
- **No outbound channel-init.** The plugin only sends replies (in response to inbound A2A); starting a fresh A2A conversation initiated FROM the Claude Code side requires a future `start_workspace_chat` tool.
|
||||||
|
|
||||||
|
Track the v0.2 roadmap on the [plugin repo's README](https://github.com/Molecule-AI/molecule-mcp-claude-channel#limitations-v01).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [External Agent Registration](/docs/guides/external-agent-registration) — full A2A wire-shape reference + Python SDK + curl flow
|
||||||
|
- [External Workspace Quickstart](/docs/guides/external-workspace-quickstart) — 5-min guide for any HTTP-speaking agent
|
||||||
|
- [Remote Workspaces FAQ](/docs/guides/remote-workspaces-faq) — production hardening notes
|
||||||
|
- [`Molecule-AI/molecule-mcp-claude-channel`](https://github.com/Molecule-AI/molecule-mcp-claude-channel) — plugin source code, issues, v0.2 roadmap
|
||||||
@ -9,6 +9,8 @@ Run an agent on your laptop, a home server, a cloud VM, or any machine with inte
|
|||||||
|
|
||||||
> **Looking for the operator-focused reference?** See [External Agent Registration](/docs/guides/external-agent-registration) for full capability + auth details, or [Remote Workspaces FAQ](/docs/guides/remote-workspaces-faq) for hardening + production notes. This doc is the fast path.
|
> **Looking for the operator-focused reference?** See [External Agent Registration](/docs/guides/external-agent-registration) for full capability + auth details, or [Remote Workspaces FAQ](/docs/guides/remote-workspaces-faq) for hardening + production notes. This doc is the fast path.
|
||||||
|
|
||||||
|
> **Running Claude Code on your laptop?** Skip this guide — use the [Claude Code Channel Plugin](/docs/guides/claude-code-channel-plugin) instead. It's polling-based and needs no tunnel, so your laptop session shows up on the canvas in under a minute.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## What is an "external workspace"?
|
## What is an "external workspace"?
|
||||||
|
|||||||
166
content/docs/marketplace.mdx
Normal file
166
content/docs/marketplace.mdx
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
---
|
||||||
|
title: Marketplace
|
||||||
|
description: A tiered library of plugins, agents, and bundles you can mount into any Molecule workspace.
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The Molecule **Marketplace** is the distribution surface for reusable agent
|
||||||
|
infrastructure. It surfaces three tiers of artifacts — from a single MCP
|
||||||
|
plugin to a full team topology — and the same governance, memory, and audit
|
||||||
|
substrate runs underneath each one.
|
||||||
|
|
||||||
|
You browse and install via the Marketplace UI at
|
||||||
|
[`https://moleculesai.app`](https://moleculesai.app), or pin entries from
|
||||||
|
your `workspace.yaml` for reproducible deployments.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Three Tiers
|
||||||
|
|
||||||
|
| Tier | Name | Granularity | Mount as |
|
||||||
|
|------|------|-------------|----------|
|
||||||
|
| **L1** | Plugins | A single MCP server / tool pack | Tool capability on an agent or workspace |
|
||||||
|
| **L2** | Agents | A prebuilt single-agent skill (prompts + tools + policy) | Workspace member |
|
||||||
|
| **L3** | Bundles | A full team topology (root + children with their own scopes) | Workspace |
|
||||||
|
|
||||||
|
The tier model is intentionally additive — an L3 Bundle is composed of L2
|
||||||
|
Agents, which in turn use L1 Plugins. Forking a Bundle gives you the lineage
|
||||||
|
to swap any constituent piece without rewiring the operating model.
|
||||||
|
|
||||||
|
### L1 — Plugins
|
||||||
|
|
||||||
|
Plugins are MCP servers or agentskills.io packs. Examples:
|
||||||
|
|
||||||
|
- `postgres` — read/write Postgres with role-scoped credentials
|
||||||
|
- `slack` — post and search Slack with workspace-scoped tokens
|
||||||
|
- `linear` — create / triage / comment on Linear issues
|
||||||
|
- `gh-actions` — query and dispatch GitHub Actions runs
|
||||||
|
- `sentry` — read incident timeline, ack alerts
|
||||||
|
|
||||||
|
Plugins follow the [two-axis source/shape model](/docs/plugins) and install
|
||||||
|
from either a curated `local://` source or a pinned `github://owner/repo#tag`.
|
||||||
|
|
||||||
|
### L2 — Agents
|
||||||
|
|
||||||
|
Agents are single-purpose skills mounted as a workspace member. They ship with:
|
||||||
|
|
||||||
|
- A **system prompt** baked in
|
||||||
|
- A **tool manifest** specifying which L1 plugins they require
|
||||||
|
- A **policy** declaring scope reads/writes and approval requirements
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- `code-reviewer` — five-axis review, posts inline comments via `gh-actions`
|
||||||
|
- `oncall-triager` — reads Sentry, drafts a runbook step, requests approval before paging
|
||||||
|
- `churn-analyst` — periodic Postgres + Stripe rollup, posts a weekly Slack summary
|
||||||
|
|
||||||
|
Mount an agent via the workspace UI or `workspace.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
members:
|
||||||
|
- kind: agent
|
||||||
|
source: marketplace://l2/code-reviewer
|
||||||
|
version: ^1.2.0
|
||||||
|
scopes:
|
||||||
|
- read: pull_requests
|
||||||
|
- write: pull_request_comments
|
||||||
|
```
|
||||||
|
|
||||||
|
### L3 — Bundles
|
||||||
|
|
||||||
|
Bundles are complete team topologies. A bundle ships:
|
||||||
|
|
||||||
|
- A **root agent** that coordinates the team
|
||||||
|
- One or more **child agents**, each with its own scope, memory, and tool
|
||||||
|
list
|
||||||
|
- A **policy graph** declaring which scopes the root can write through and
|
||||||
|
which approvals route to humans
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- `growth-team` — root strategist + content-writer + analytics-rollup +
|
||||||
|
experiment-designer
|
||||||
|
- `platform-ops` — root SRE + on-call triager + change-reviewer +
|
||||||
|
incident-scribe
|
||||||
|
- `revenue-pod` — root commercial lead + churn-analyst + cs-summarizer +
|
||||||
|
expansion-prospector
|
||||||
|
|
||||||
|
Mount a bundle as a workspace:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
workspace:
|
||||||
|
bundle: marketplace://l3/platform-ops
|
||||||
|
bundle_version: ^0.4.0
|
||||||
|
overrides:
|
||||||
|
members:
|
||||||
|
change-reviewer:
|
||||||
|
scopes:
|
||||||
|
- read: ["github:Molecule-AI/*", "linear:eng"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Forking is encouraged — the bundle author publishes the operating model;
|
||||||
|
your team tunes it for your processes without rebuilding the substrate.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Trust Tiers
|
||||||
|
|
||||||
|
Every Marketplace entry carries a **trust tier** that signals review depth
|
||||||
|
and supply-chain provenance:
|
||||||
|
|
||||||
|
| Trust | Vetting | Provenance |
|
||||||
|
|-------|---------|------------|
|
||||||
|
| **Verified** | Reviewed by Molecule for safety, prompt-injection resistance, and policy correctness | Published from a Molecule-controlled identity |
|
||||||
|
| **Partner** | Reviewed by a Marketplace partner; carries the partner's identity badge | Published from a verified partner account |
|
||||||
|
| **Community** | Self-published; static analysis + sandbox runtime; no human review | Pinned to a specific commit SHA |
|
||||||
|
|
||||||
|
The trust tier is shown on every listing card and gated by enterprise
|
||||||
|
policy: organizations on the Enterprise plan can restrict installs to
|
||||||
|
Verified-only via `policy.marketplace.min_trust = verified`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installing from the Marketplace
|
||||||
|
|
||||||
|
Browse listings at [`https://moleculesai.app`](https://moleculesai.app).
|
||||||
|
Each card shows tier (L1/L2/L3), trust badge, runtime compatibility, and
|
||||||
|
required scopes. The "Install" flow:
|
||||||
|
|
||||||
|
1. Picks a workspace (or creates a new one) to mount into.
|
||||||
|
2. Surfaces required scopes for review and approval.
|
||||||
|
3. Pins to a specific version (semver range, exact tag, or commit SHA).
|
||||||
|
4. Writes the entry into your `workspace.yaml` and triggers a workspace
|
||||||
|
redeploy.
|
||||||
|
|
||||||
|
You can also install non-interactively:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST https://app.moleculesai.app/cp/orgs/$ORG/marketplace/install \
|
||||||
|
-H "Authorization: Bearer $CP_ADMIN_API_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"tier": "l2",
|
||||||
|
"slug": "code-reviewer",
|
||||||
|
"version": "^1.2.0",
|
||||||
|
"workspace_id": "ws_abc123"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Listing on the Marketplace
|
||||||
|
|
||||||
|
If you have built reusable agent infrastructure — a plugin, agent, or
|
||||||
|
bundle — you can list it on the Marketplace and reach every Molecule
|
||||||
|
organization. See [Listing on the Marketplace](/docs/marketplace/creators)
|
||||||
|
for the full builder workflow.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [Plugins](/docs/plugins) — L1 source/shape model and install mechanics
|
||||||
|
- [External Agents](/docs/external-agents) — bringing a non-Molecule agent runtime
|
||||||
|
- [Workspace Configuration](/docs/workspace-config) — `workspace.yaml` reference
|
||||||
|
- [Listing on the Marketplace](/docs/marketplace/creators) — builder workflow
|
||||||
164
content/docs/marketplace/creators.mdx
Normal file
164
content/docs/marketplace/creators.mdx
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
---
|
||||||
|
title: Listing on the Marketplace
|
||||||
|
description: How builders ship plugins, agents, and bundles to every Molecule organization.
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The Marketplace is open to external builders. If you have authored reusable
|
||||||
|
agent infrastructure — an MCP plugin, a single-agent skill, or a full team
|
||||||
|
bundle — you can list it and reach every Molecule organization. We handle
|
||||||
|
distribution, billing, and policy; you keep the IP and the upgrade cadence.
|
||||||
|
|
||||||
|
This page walks through the three-step workflow: **Build · List · Earn**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Build
|
||||||
|
|
||||||
|
You author your artifact against the open Molecule SDK. The same primitives
|
||||||
|
we use internally are available to you:
|
||||||
|
|
||||||
|
- **Workspace** — the durable boundary for memory, members, and policy
|
||||||
|
- **A2A** — agent-to-agent messaging, used to talk to runtimes you don't
|
||||||
|
own (LangGraph, CrewAI, etc.)
|
||||||
|
- **Memory scopes** — hierarchical, governance-aware persistence
|
||||||
|
- **Audit** — every action is captured at the orchestration layer
|
||||||
|
|
||||||
|
Pick the tier that matches your artifact's granularity:
|
||||||
|
|
||||||
|
### L1 — Plugins
|
||||||
|
|
||||||
|
A plugin is an MCP server (or an agentskills.io pack). The
|
||||||
|
[two-axis source/shape model](/docs/plugins) describes how the workspace
|
||||||
|
runtime loads it. Authoring requirements:
|
||||||
|
|
||||||
|
- A `plugin.yaml` manifest declaring tools, required scopes, and runtime
|
||||||
|
compatibility.
|
||||||
|
- A README documenting the tool surface and side effects.
|
||||||
|
- For MCP plugins: an HTTP or stdio MCP server pinned to a tagged commit.
|
||||||
|
|
||||||
|
A reference plugin lives at
|
||||||
|
[`Molecule-AI/molecule-ai-plugin-template`](https://github.com/Molecule-AI/molecule-ai-plugin-template).
|
||||||
|
|
||||||
|
### L2 — Agents
|
||||||
|
|
||||||
|
An agent is a single workspace member with a baked-in prompt + tools +
|
||||||
|
policy. Authoring requirements:
|
||||||
|
|
||||||
|
- An `agent.yaml` manifest declaring system prompt, required L1 plugins,
|
||||||
|
scope reads/writes, and approval triggers.
|
||||||
|
- A `prompts/` directory with the system prompt and any reusable templates.
|
||||||
|
- A `tests/` directory exercising the prompt against canned scenarios.
|
||||||
|
|
||||||
|
Reference: [`Molecule-AI/molecule-ai-agent-template`](https://github.com/Molecule-AI/molecule-ai-agent-template).
|
||||||
|
|
||||||
|
### L3 — Bundles
|
||||||
|
|
||||||
|
A bundle ships a complete team topology — a root agent plus children, each
|
||||||
|
with its own scope and memory. Authoring requirements:
|
||||||
|
|
||||||
|
- A `bundle.yaml` declaring members, their scopes, and the policy graph
|
||||||
|
(which scopes the root can write through, which approvals route to
|
||||||
|
humans).
|
||||||
|
- A `members/` directory containing member-specific overrides if any
|
||||||
|
member is a fork of an L2 agent.
|
||||||
|
- A `topology.svg` diagram (auto-rendered from `bundle.yaml`, but you can
|
||||||
|
override).
|
||||||
|
|
||||||
|
Reference: [`Molecule-AI/molecule-ai-bundle-template`](https://github.com/Molecule-AI/molecule-ai-bundle-template).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. List
|
||||||
|
|
||||||
|
Submit through the **Creator Portal** at
|
||||||
|
[`https://moleculesai.app/creators`](https://moleculesai.app/creators).
|
||||||
|
The submission flow:
|
||||||
|
|
||||||
|
1. **Connect** — link the GitHub repository hosting your artifact. We pull
|
||||||
|
from tagged releases; we never re-tag or modify your code.
|
||||||
|
2. **Manifest check** — we validate `plugin.yaml` / `agent.yaml` /
|
||||||
|
`bundle.yaml` against the schema for your tier and surface any gaps.
|
||||||
|
3. **Static analysis** — credential-shape scan, prompt-injection-pattern
|
||||||
|
scan, and dependency vulnerability check on every tagged release.
|
||||||
|
4. **Sandbox boot** — your artifact is mounted into a throwaway workspace
|
||||||
|
to verify it boots, declares its scopes correctly, and surfaces a
|
||||||
|
reasonable error path.
|
||||||
|
5. **Trust tier** — every artifact starts at **Community**. Apply for
|
||||||
|
**Partner** or **Verified** review once you have a couple of releases
|
||||||
|
under your belt.
|
||||||
|
|
||||||
|
Pricing is configured at submission:
|
||||||
|
|
||||||
|
- **Free** — no charge to install.
|
||||||
|
- **Per-seat** — a flat monthly amount per workspace member that mounts
|
||||||
|
the artifact.
|
||||||
|
- **Per-use** — metered against a unit you define (token calls, runs,
|
||||||
|
alerts handled).
|
||||||
|
- **Hybrid** — base seat fee plus metered overages.
|
||||||
|
|
||||||
|
You can change pricing on subsequent releases; existing installs are
|
||||||
|
grandfathered to the version they pinned.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Earn
|
||||||
|
|
||||||
|
Once your listing is live, you receive:
|
||||||
|
|
||||||
|
- **Distribution** — every Molecule organization sees your listing in the
|
||||||
|
Marketplace UI, gated only by their policy (`min_trust`, region, etc.).
|
||||||
|
- **Billing** — Molecule handles the charge to the installing
|
||||||
|
organization, deducts the platform fee (15% as of writing; check the
|
||||||
|
current rate in the Creator Portal), and pays out monthly.
|
||||||
|
- **Audit visibility** — you see install counts, version distribution,
|
||||||
|
and aggregated usage metrics in the Creator Portal. You do **not** see
|
||||||
|
per-organization data.
|
||||||
|
- **Upgrade cadence** — semver: bump tags, organizations on a `^range`
|
||||||
|
pin pull updates on their next workspace redeploy. Major bumps require
|
||||||
|
re-approval of any new scopes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Policy & Safety
|
||||||
|
|
||||||
|
By listing, you agree to:
|
||||||
|
|
||||||
|
- **No exfiltration** — your code does not transmit organization data
|
||||||
|
outside the scopes it declares.
|
||||||
|
- **Pinned releases** — every version is pinned to an immutable commit;
|
||||||
|
retagging is not permitted.
|
||||||
|
- **Disclose model usage** — if your agent calls an LLM API, declare the
|
||||||
|
provider and model so enterprise plans can route through their own
|
||||||
|
keys.
|
||||||
|
- **Respect approval triggers** — if your `agent.yaml` declares a scope
|
||||||
|
that requires human approval (e.g. `write: pull_request_merge`), you
|
||||||
|
must call the approval API before acting.
|
||||||
|
|
||||||
|
Listings that violate these terms are de-listed; refunds for affected
|
||||||
|
installs are paid from your account.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
Once a listing is live, you can:
|
||||||
|
|
||||||
|
- Push new tagged releases — they enter the static-analysis + sandbox
|
||||||
|
flow automatically.
|
||||||
|
- Mark older versions as **deprecated** to nudge installs to upgrade.
|
||||||
|
- File **security advisories** that surface to every organization on a
|
||||||
|
vulnerable pinned version.
|
||||||
|
- Yank a release in the rare case of a critical bug; organizations
|
||||||
|
pinned to the yanked tag are notified and offered the next safe version.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [Marketplace](/docs/marketplace) — tier model and installation overview
|
||||||
|
- [Plugins](/docs/plugins) — L1 plugin source/shape mechanics
|
||||||
|
- [Workspace Configuration](/docs/workspace-config) — pinning marketplace
|
||||||
|
entries in `workspace.yaml`
|
||||||
|
- [Security » OWASP Agentic Top 10](/docs/security/owasp-agentic-top-10) — supply-chain considerations relevant to bundle authors
|
||||||
@ -20,6 +20,9 @@
|
|||||||
"self-hosting/admin-token",
|
"self-hosting/admin-token",
|
||||||
"observability",
|
"observability",
|
||||||
"troubleshooting",
|
"troubleshooting",
|
||||||
|
"---Marketplace---",
|
||||||
|
"marketplace",
|
||||||
|
"marketplace/creators",
|
||||||
"---Security---",
|
"---Security---",
|
||||||
"security/index",
|
"security/index",
|
||||||
"security/safe-mcp-advisory",
|
"security/safe-mcp-advisory",
|
||||||
|
|||||||
@ -104,14 +104,15 @@ Cline) and restart the client.
|
|||||||
|
|
||||||
## Optional — declare your identity & capabilities
|
## Optional — declare your identity & capabilities
|
||||||
|
|
||||||
Three additional env vars control how your workspace appears on the
|
Four additional env vars control how your workspace appears on the
|
||||||
canvas and to peer agents calling `list_peers`:
|
canvas and how the wheel's inbound-delivery contract behaves:
|
||||||
|
|
||||||
| Env var | What it sets | Default |
|
| Env var | What it sets | Default |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `MOLECULE_AGENT_NAME` | Display name on the canvas card | `molecule-mcp-{id[:8]}` |
|
| `MOLECULE_AGENT_NAME` | Display name on the canvas card | `molecule-mcp-{id[:8]}` |
|
||||||
| `MOLECULE_AGENT_DESCRIPTION` | One-line description in Details/Skills tabs | empty |
|
| `MOLECULE_AGENT_DESCRIPTION` | One-line description in Details/Skills tabs | empty |
|
||||||
| `MOLECULE_AGENT_SKILLS` | Comma-separated skill names — e.g. `research,code-review,memory-curation` | `[]` |
|
| `MOLECULE_AGENT_SKILLS` | Comma-separated skill names — e.g. `research,code-review,memory-curation` | `[]` |
|
||||||
|
| `MOLECULE_MCP_POLL_TIMEOUT_SECS` | How long the agent blocks on `wait_for_message` per turn (the universal poll path). `0` disables polling for push-only mode (Claude Code with `--dangerously-load-development-channels`). Above 60 clamps to 60. | `2` |
|
||||||
|
|
||||||
Skills are surfaced two places:
|
Skills are surfaced two places:
|
||||||
|
|
||||||
@ -158,7 +159,7 @@ status. If the workspace is still offline after ~30s, check
|
|||||||
| `delegate_task` | Send a task to a peer and wait for the reply |
|
| `delegate_task` | Send a task to a peer and wait for the reply |
|
||||||
| `delegate_task_async` | Fire-and-forget delegation; result lands in inbox |
|
| `delegate_task_async` | Fire-and-forget delegation; result lands in inbox |
|
||||||
| `check_task_status` | Poll an async delegation |
|
| `check_task_status` | Poll an async delegation |
|
||||||
| `wait_for_message` | Block until the next inbound A2A message arrives |
|
| `wait_for_message` | Block until the next inbound A2A message arrives — the universal inbound-delivery primitive (see [Inbound delivery](#inbound-delivery-universal-poll-optional-push)) |
|
||||||
| `inbox_peek` / `inbox_pop` | Inspect / acknowledge queued inbound messages |
|
| `inbox_peek` / `inbox_pop` | Inspect / acknowledge queued inbound messages |
|
||||||
| `send_message_to_user` | Push a chat bubble to the user's canvas |
|
| `send_message_to_user` | Push a chat bubble to the user's canvas |
|
||||||
| `commit_memory` / `recall_memory` | Persistent KV (local / team / global scope) |
|
| `commit_memory` / `recall_memory` | Persistent KV (local / team / global scope) |
|
||||||
@ -168,29 +169,63 @@ External runtimes can't accept inbound HTTP, so the wheel polls
|
|||||||
through `wait_for_message` + `inbox_peek` / `inbox_pop`. Use those
|
through `wait_for_message` + `inbox_peek` / `inbox_pop`. Use those
|
||||||
instead of waiting for an HTTP webhook — there isn't one.
|
instead of waiting for an HTTP webhook — there isn't one.
|
||||||
|
|
||||||
### Push-UX for notification-capable hosts
|
### Inbound delivery: universal poll, optional push
|
||||||
|
|
||||||
On top of the polling tools, the wheel emits a JSON-RPC notification
|
Inbound messages reach the agent via one of two paths. The wheel
|
||||||
(`notifications/claude/channel`) on every new inbound message. Hosts
|
exposes both; which one fires depends on the host's capabilities.
|
||||||
that recognise that method (Claude Code today; any compliant client
|
Both paths converge on the same `inbox_pop` ack so dedup is automatic.
|
||||||
tomorrow) treat the notification as a conversation interrupt — the
|
|
||||||
message text becomes the next agent turn without the agent having to
|
|
||||||
call `wait_for_message` first.
|
|
||||||
|
|
||||||
Hosts that don't recognise the method silently ignore it, so the same
|
**Poll path (universal default — works on every spec-compliant MCP
|
||||||
wheel works for both push-capable and poll-only runtimes. There is no
|
client).** The wheel's `initialize` handshake includes an `instructions`
|
||||||
config flag to toggle: pollers keep polling, notification-capable hosts
|
field telling the agent: *"At the start of every turn, before producing
|
||||||
get push automatically.
|
your final response, call `wait_for_message(timeout_secs=N)` to check
|
||||||
|
for inbound messages."* Every MCP client surfaces `instructions` to
|
||||||
|
the agent's system prompt automatically, so Claude Code, Cursor, Cline,
|
||||||
|
OpenCode, hermes-agent, and codex all receive the polling contract
|
||||||
|
without any per-client wiring. The 2-second default is tuned for the
|
||||||
|
"peer A2A landed seconds before my turn started" common case; tune
|
||||||
|
via the `MOLECULE_MCP_POLL_TIMEOUT_SECS` env var
|
||||||
|
(see "Optional — declare your identity & capabilities" above).
|
||||||
|
|
||||||
|
**Push path (Claude Code with channel push enabled — strictly
|
||||||
|
better when available).** On top of the poll path, the wheel emits a
|
||||||
|
JSON-RPC notification (`notifications/claude/channel`) on every new
|
||||||
|
inbound message and declares the matching `experimental.claude/channel`
|
||||||
|
capability in `initialize`. Claude Code with channel push enabled
|
||||||
|
turns the notification into an inline `<channel source="molecule"
|
||||||
|
...>` synthetic user turn — zero agent-side polling cost, zero
|
||||||
|
per-turn stall.
|
||||||
|
|
||||||
|
**Today (research preview), Claude Code's channel push requires
|
||||||
|
either the `--dangerously-load-development-channels` launch flag OR
|
||||||
|
an entry on Claude Code's approved channel-server allowlist.** The
|
||||||
|
wheel ships the wire shape correctly, but a standard `claude` launch
|
||||||
|
without the flag silently drops the notification — which is why the
|
||||||
|
poll path has to be the floor.
|
||||||
|
|
||||||
|
Set `MOLECULE_MCP_POLL_TIMEOUT_SECS=0` to disable polling entirely
|
||||||
|
when you're running Claude Code with the dev-channels flag and don't
|
||||||
|
want the per-turn stall. The instructions adapt automatically: with
|
||||||
|
polling disabled, the agent is told push is the only delivery path.
|
||||||
|
|
||||||
|
| Client | Push path | Poll path |
|
||||||
|
|---|---|---|
|
||||||
|
| Claude Code with `--dangerously-load-development-channels` | ✅ inline tag | ✅ also works |
|
||||||
|
| Claude Code (standard launch) | ❌ silently dropped | ✅ via instructions |
|
||||||
|
| Cursor / Cline / OpenCode / codex | ❌ method ignored | ✅ via instructions |
|
||||||
|
| hermes-agent | ❌ method ignored | ✅ naturally polls every cycle |
|
||||||
|
|
||||||
### MCP spec compliance
|
### MCP spec compliance
|
||||||
|
|
||||||
The wheel speaks MCP protocol version **2024-11-05** over stdio
|
The wheel speaks MCP protocol version **2024-11-05** over stdio
|
||||||
JSON-RPC, declaring only the `tools` capability. It implements the
|
JSON-RPC. It declares the standard `tools` capability plus the
|
||||||
standard request methods and nothing client-specific:
|
`experimental.claude/channel` capability for the optional push path
|
||||||
|
(see [Inbound delivery](#inbound-delivery-universal-poll-optional-push)).
|
||||||
|
It implements the standard request methods and nothing client-specific:
|
||||||
|
|
||||||
| MCP method | Behavior |
|
| MCP method | Behavior |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `initialize` | Echoes `protocolVersion: "2024-11-05"`, `serverInfo`, declares `tools` capability |
|
| `initialize` | Echoes `protocolVersion: "2024-11-05"`, `serverInfo`, declares `tools` + `experimental.claude/channel` capabilities, returns the dual-path delivery `instructions` |
|
||||||
| `notifications/initialized` | No-op (no response — per spec) |
|
| `notifications/initialized` | No-op (no response — per spec) |
|
||||||
| `tools/list` | Returns all exposed tools in one response (no pagination cursor — surface is small) |
|
| `tools/list` | Returns all exposed tools in one response (no pagination cursor — surface is small) |
|
||||||
| `tools/call` | Dispatches by name, returns `content: [{ type: "text", text: ... }]` |
|
| `tools/call` | Dispatches by name, returns `content: [{ type: "text", text: ... }]` |
|
||||||
@ -198,8 +233,10 @@ standard request methods and nothing client-specific:
|
|||||||
|
|
||||||
The push-UX notification (`notifications/claude/channel`) is the only
|
The push-UX notification (`notifications/claude/channel`) is the only
|
||||||
non-standard method emitted, and it's a one-way notification — clients
|
non-standard method emitted, and it's a one-way notification — clients
|
||||||
that don't handle it discard it per JSON-RPC semantics. No part of the
|
that don't handle it discard it per JSON-RPC semantics. The poll path
|
||||||
wheel's tool surface depends on a client recognizing it.
|
(via the standard `instructions` field) carries delivery for those
|
||||||
|
clients, so no part of the wheel's tool surface depends on a client
|
||||||
|
recognizing the notification.
|
||||||
|
|
||||||
This means **any spec-compliant MCP client** can drive the wheel:
|
This means **any spec-compliant MCP client** can drive the wheel:
|
||||||
Claude Code, Cursor, Cline, OpenCode, hermes-agent, or anything else
|
Claude Code, Cursor, Cline, OpenCode, hermes-agent, or anything else
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user