diff --git a/apps/docs/docs.json b/apps/docs/docs.json index 2b47bbae22..f05ef3ec2f 100644 --- a/apps/docs/docs.json +++ b/apps/docs/docs.json @@ -33,7 +33,7 @@ }, { "group": "Hosted", - "pages": ["hosted/cloud", "hosted/docker", "hosted/cloudflare", "hosted/tracing"] + "pages": ["hosted/cloud", "hosted/docker", "hosted/tracing"] }, { "group": "Concepts", diff --git a/apps/docs/hosted/cloudflare.mdx b/apps/docs/hosted/cloudflare.mdx deleted file mode 100644 index d463ec6427..0000000000 --- a/apps/docs/hosted/cloudflare.mdx +++ /dev/null @@ -1,151 +0,0 @@ ---- -title: Self Hosted Cloudflare -description: "Run Executor as a single Cloudflare Worker in your own account, with Cloudflare Access for auth and D1 for storage." ---- - -Executor runs as a single Cloudflare Worker. Authentication is handled entirely by -[Cloudflare Access](https://developers.cloudflare.com/cloudflare-one/policies/access/) -(there is no separate app login), storage is [D1](https://developers.cloudflare.com/d1/), -tool code runs in QuickJS inside the Worker, and the web console, API, and MCP -endpoint are all served from that one Worker. - -It is single-tenant: every Access-verified user belongs to the one configured org, -and you manage members and credentials in Cloudflare Access rather than in the app. - -## Prerequisites - -- A Cloudflare account with Workers and Zero Trust (Access) enabled. -- The [Executor repo](https://github.com/UsefulSoftwareCo/executor) checked out, with - dependencies installed (`bun install`). - -## Deploy - -From `apps/host-cloudflare`: - -```bash -bunx wrangler login -bun run deploy:setup -``` - -`deploy:setup` is idempotent: it creates or reuses the `executor` D1 database, -writes its id into `wrangler.jsonc`, generates and uploads the `EXECUTOR_SECRET_KEY` -secret, and deploys the Worker. It then prints the one remaining manual step. - -## Put it behind Cloudflare Access - -Until the Worker is behind an Access application, every API and MCP route returns -`401`. In the Zero Trust dashboard: - -1. Go to **Access → Applications → Add an application → Self-hosted**. -2. Set the application domain to `executor-cloudflare..workers.dev`. -3. Add an Access policy (for example, emails ending in `@yourcompany.com`). -4. Copy the application **Audience (AUD)** tag, then redeploy with your Access settings: - - ```bash - bunx wrangler deploy \ - --var ACCESS_AUD: \ - --var ACCESS_TEAM_DOMAIN:.cloudflareaccess.com - ``` - - You can also set `ACCESS_AUD` and `ACCESS_TEAM_DOMAIN` in `wrangler.jsonc` and - redeploy. - -Now visiting the Worker prompts a Cloudflare Access login, and the Worker validates -the issued JWT on every request. - -## Connect an agent - -The Worker serves a streamable-HTTP MCP endpoint at `/mcp`, gated by Access like the -rest of the surfaces. Point your client at -`https://executor-cloudflare..workers.dev/mcp`. - -Because Access sits in front of the Worker, an MCP client that reaches `/mcp` -without a valid Access credential is served Cloudflare's HTML login page instead of -the endpoint. The streamable-HTTP client rejects that with -`Streamable HTTP error: Unexpected content type: text/html`: that is the Access login -page, not a Worker response. A browser passes Access via an interactive login, but an -MCP client cannot follow that redirect, so it has to authenticate another way. Choose -the path that matches your client. - -### For interactive clients: Managed OAuth (recommended) - -Cloudflare Access can run the MCP OAuth flow on the Worker's behalf, so a client -like Claude authenticates with a normal login popup and no manual headers. The Worker -keeps doing exactly what it already does (validate the `Cf-Access-Jwt-Assertion`); no -code or redeploy is needed. - -1. Open the self-hosted Access application gating the Worker (the one from the - previous section). -2. Enable **Managed OAuth** in the application's settings (the option that lets - non-browser clients authenticate over OAuth). With it on, Access answers an - unauthenticated `/mcp` request with an OAuth `401` challenge and serves the OAuth - discovery, authorize, and token endpoints itself, instead of the HTML login page. - Keep the Access application scoped to the whole Worker hostname (as set in the - previous section), not a single path like `/mcp`: the discovery probes clients - send to `/.well-known/oauth-authorization-server` and - `/.well-known/oauth-protected-resource` then land on Access too, which answers - them rather than letting them fall through to the Worker's SPA fallback (which the - Worker itself does not serve, so a path-scoped application would return HTML or - `404` there). -3. Point your client at the `/mcp` URL with no extra configuration. On connect it - discovers the OAuth endpoints, opens a browser popup to log in through Access, and - Cloudflare injects the validated JWT into every subsequent request. - - ```bash - npx add-mcp https://executor-cloudflare..workers.dev/mcp \ - --transport http --name executor - ``` - -This is the same flow Cloudflare documents in -[Secure MCP servers with Access](https://developers.cloudflare.com/cloudflare-one/access-controls/ai-controls/secure-mcp-servers/). - -If a client still won't start the OAuth flow after you enable Managed OAuth (it -classifies `/mcp` as not an MCP endpoint, or you still hit the -`Unexpected content type: text/html` error), its probe isn't consuming Access's -`401` challenge. Fall back to the service-token path below: it authenticates with -request headers and needs no client-side OAuth discovery, so it works regardless of -how the client handles the challenge. - -### For headless clients: service token - -When no human is present to complete the OAuth popup (CI, a background agent), -authenticate machine-to-machine with an Access service token instead. - -1. In the Zero Trust dashboard, go to **Access → Service Auth → Service Tokens → - Create Service Token**. Copy the **Client ID** and **Client Secret** (the secret - is shown once). -2. On the Access application gating the Worker, add a policy with **Action: Service - Auth** that includes that service token (Include → Service Token → your token). - The `Service Auth` action lets the token through without an interactive IdP login. -3. Configure your MCP client to send the token on every request as the - `CF-Access-Client-Id` and `CF-Access-Client-Secret` headers. Access exchanges them - for the `Cf-Access-Jwt-Assertion` JWT (carrying the token's `common_name`) that - the Worker validates, exactly like a human login. - - With [`add-mcp`](https://www.npmjs.com/package/add-mcp), which writes the config - for whichever agents it detects: - - ```bash - npx add-mcp https://executor-cloudflare..workers.dev/mcp \ - --transport http --name executor \ - --header "CF-Access-Client-Id: .access" \ - --header "CF-Access-Client-Secret: " - ``` - - For an `mcpServers` config block: - - ```json - { - "executor": { - "type": "http", - "url": "https://executor-cloudflare..workers.dev/mcp", - "headers": { - "CF-Access-Client-Id": ".access", - "CF-Access-Client-Secret": "" - } - } - } - ``` - -Once connected, see [MCP Proxy](/mcp-proxy) for how the endpoint exposes your -integrations. diff --git a/apps/docs/index.mdx b/apps/docs/index.mdx index 3d11c5cd3d..362d9a4547 100644 --- a/apps/docs/index.mdx +++ b/apps/docs/index.mdx @@ -14,7 +14,7 @@ Choose how to run Executor: - **[CLI](/local/cli)**: a background service on your machine, from the terminal - **[Desktop app](/local/desktop)**: the same runtime, as a desktop app - **[Executor Cloud](/hosted/cloud)**: hosted, with a free tier; nothing to run yourself -- **Self-host**: your own infrastructure, via [Docker](/hosted/docker) or [Cloudflare](/hosted/cloudflare) +- **Self-host**: your own infrastructure, via [Docker](/hosted/docker) ## Set up with your agent diff --git a/apps/docs/mcp-proxy.mdx b/apps/docs/mcp-proxy.mdx index c142f31b3b..14e98d6804 100644 --- a/apps/docs/mcp-proxy.mdx +++ b/apps/docs/mcp-proxy.mdx @@ -52,7 +52,7 @@ Executor: - **Local:** see [CLI](/local/cli) for `executor mcp` and the `add-mcp` command. - **Hosted:** see [Executor Cloud](/hosted/cloud), or self-host on - [Docker](/hosted/docker) or [Cloudflare](/hosted/cloudflare). + [Docker](/hosted/docker). Once a client is connected, every integration you add to Executor appears in that agent automatically. diff --git a/apps/marketing/public/llms.txt b/apps/marketing/public/llms.txt index d98199e407..c821235911 100644 --- a/apps/marketing/public/llms.txt +++ b/apps/marketing/public/llms.txt @@ -11,7 +11,7 @@ How it works: - Set policies: decide whether each tool is always allowed, needs approval, or is blocked. - Use it from any MCP-compatible agent: one catalog of tools, shared across every client. -Run it your way: local CLI, a desktop app, hosted Executor Cloud, or self-hosted on Docker or Cloudflare. +Run it your way: local CLI, a desktop app, hosted Executor Cloud, or self-hosted on Docker. ## Docs @@ -23,6 +23,14 @@ Run it your way: local CLI, a desktop app, hosted Executor Cloud, or self-hosted - [Pricing](https://executor.sh/#pricing): plans for Executor Cloud. - [Install](https://executor.sh/#install): install the CLI and connect your first agent. +## For agents + +Markdown representations of the pages above, for machines rather than browsers. + +- [/index.md](https://executor.sh/index.md): the homepage as Markdown — what Executor is, how it works, what you get, ways to run it, pricing, and FAQ. +- [/setup-prompt.md](https://executor.sh/setup-prompt.md): a prompt to paste into a coding agent. It picks the right form of Executor, installs it, connects over MCP, and gets a first connection working. +- [/pricing.md](https://executor.sh/pricing.md): the Executor Cloud plans as Markdown. + ## Source - [GitHub](https://github.com/UsefulSoftwareCo/executor): source for the integration layer, plugins, and hosts. diff --git a/apps/marketing/src/assets/rhys-sullivan.jpg b/apps/marketing/src/assets/rhys-sullivan.jpg new file mode 100644 index 0000000000..0a98109c47 Binary files /dev/null and b/apps/marketing/src/assets/rhys-sullivan.jpg differ diff --git a/apps/marketing/src/components/variants/doc-body.astro b/apps/marketing/src/components/variants/doc-body.astro new file mode 100644 index 0000000000..a9727876a1 --- /dev/null +++ b/apps/marketing/src/components/variants/doc-body.astro @@ -0,0 +1,235 @@ +--- +// Document-style homepage body. Reads like a short paper about the product: +// one modest headline, plain first-person prose, six beats separated by +// hairlines, the two demos kept as numbered figures, and one way to start. +// Sits inside the rail layout; the rail's "On this page" list points at the +// section ids below. +import { AnimatedBeamDemo } from "../animated-beam-demo"; +import { ContextBloatDemo } from "../context-bloat-demo"; +import { setupPrompt } from "../../content/site-copy"; +import "../../styles/variants/doc.css"; +// Avatar goes through Vite so it ships as a hashed asset; new /public files +// are not picked up by the deploy pipeline. +import rhysAvatar from "../../assets/rhys-sullivan.jpg"; +const X_URL = "https://x.com/RhysSullivan"; + +interface Props { + gh: string; +} +const { gh } = Astro.props; +--- + +
+ {/* ── Intro ── */} +
+

+ Executor is an MCP gateway. Your agent points at one endpoint and reaches every tool you connect. +

+

+ Claude Code, Cursor, Codex, or anything else that speaks MCP. Any tool from any + protocol. One place to add it, authenticate it, and decide what it is allowed to do. +

+
+ + + +
+
+
+

Hosted Executor. Sign in and point your agent at one URL. Free for up to three members and 100,000 executions a month.

+
Start on Cloud
+
+ + +
+

Works with Claude Code, Cursor, Codex, ChatGPT, and any MCP client.

+
+ +
+
+ +
+
+ Figure 1. Three clients reach six tools through one + MCP endpoint. Each tool keeps the protocol it came from. +
+
+ + {/* ── 01 Mission ── */} +
+

What we think

+

+ Every agent client re-implements every integration. The same API gets wired up again + for each new tool, with its own auth and its own failure modes. Credentials end up + pasted in five places, and nothing shares an idea of what a tool is allowed to do. +

+

+ We think tools should belong to you, not to one client or one model provider. So + Executor gives every tool one shape: a name, an input schema, an output schema. An + MCP server, an OpenAPI spec, and a GraphQL API all end up the same, and any agent + calls any of them the same way. +

+

+ We also think the safe path has to be the easy path. Executor keeps the semantics it + imported, so a GET and a DELETE are not the same thing, and it runs every call in a + sandbox where a raw token never reaches the model. +

+
+ + {/* ── 02 Context ── */} +
+

Thousands of tools, one in the prompt

+

+ Executor shows the model a single tool. It searches your catalog and loads a tool's + schema only when the code calls it, so connecting more services does not grow the + prompt. Toggle the services below to see the difference. +

+
+ +
+ Figure 2. Prompt size with your selection of + integrations connected, with and without Executor. +
+
+
+ + {/* ── 03 Safety ── */} +
+

Safe by default

+
+
+
Policies come from the source.
+
+ GET versus DELETE for OpenAPI, destructiveHint for MCP, mutations for GraphQL. + Agents run the safe calls on their own and ask before the rest. You can override + any tool. +
+
+
+
Secrets never reach the model.
+
+ Calls run in an isolated JavaScript sandbox. Credentials are attached host-side + at call time and never enter the sandbox, the agent, or the model's context. +
+
+
+
Set up once, whole team has it.
+
+ Per-user credentials and shared ones. New teammates get the catalog on day one, + with the same policies. +
+
+
+
+ + {/* ── 04 Run it ── */} +
+

Run it where you want

+

+ All forms expose the same tools, packaged differently. +

+
    +
  1. + 01 +
    + Cloud. Hosted, free to start, nothing to install. + executor.sh/cloud ↗ +
    +
  2. +
  3. + 02 +
    + Desktop. A desktop app for Mac, Windows, and Linux. Everything + stays on your machine. + Latest release ↗ +
    +
  4. +
  5. + 03 +
    + CLI. A background service for headless and server environments. + npm i -g executor +
    +
  6. +
  7. + 04 +
    + Self-hosted. A Docker image. + Docs ↗ +
    +
  8. +
+
+ + {/* ── 05 Pricing ── */} +
+

Pricing

+

Cloud is free for up to three members and 100,000 executions a month. Team is $15 per member per month with unlimited executions. Enterprise adds self-hosted support, SSO and SCIM, and audit logs.

+

Full pricing ↗ · Talk to us

+
+ + {/* ── 06 Writing ── */} +
+

Writing

+ +
+ + {/* ── 07 About ── */} +
+

About

+

+ We are a small team backed by Y Combinator. We built Executor because we wanted our + own agents to reach our company's resources in a way that was not scary. Most setups + make you choose between locked down and useless, or wide open and risky. We wanted a + third option. +

+ + + + Rhys Sullivan + Founder. Say hi on X, @RhysSullivan ↗ + + +

+ More about Executor ↗ · Discord ↗ +

+
+
+ + diff --git a/apps/marketing/src/components/variants/rail-b.astro b/apps/marketing/src/components/variants/rail-b.astro new file mode 100644 index 0000000000..09c321c35c --- /dev/null +++ b/apps/marketing/src/components/variants/rail-b.astro @@ -0,0 +1,129 @@ +--- +// Rail, variant B: "Gridline rail". +// Compact and technical: mark-only wordmark, a tight link stack, an outlined +// CTA, and a numbered mono index. A full-height hairline closes the right edge. +import "../../styles/variants/rail.css"; +import ycBackedBy from "../../assets/yc-backed-by.svg?url"; +import rhysAvatar from "../../assets/rhys-sullivan.jpg"; + +const X_URL = "https://x.com/RhysSullivan"; +const EMAIL = "rhys@executor.sh"; + +interface Props { + stars: string | null; +} +const { stars } = Astro.props; + +const GH = "https://github.com/UsefulSoftwareCo/executor"; +const SIGN_IN = "/api/auth/login"; + +const sections = [ + { id: "mission", label: "What we think" }, + { id: "context", label: "Context" }, + { id: "safety", label: "Safety" }, + { id: "install", label: "Run it" }, + { id: "pricing", label: "Pricing" }, + { id: "writing", label: "Writing" }, + { id: "about", label: "About" }, +]; +const pad = (index: number) => String(index + 1).padStart(2, "0"); +--- + + + + diff --git a/apps/marketing/src/components/variants/rail-spy.ts b/apps/marketing/src/components/variants/rail-spy.ts new file mode 100644 index 0000000000..817e311f22 --- /dev/null +++ b/apps/marketing/src/components/variants/rail-spy.ts @@ -0,0 +1,80 @@ +// Scroll-spy for the sidebar rail. +// +// The rail renders a `.rail__toc` whose links point at the page's section +// anchors. This marks the link for the section the reader is in with +// `aria-current="true"`, and mirrors the id onto the aside as +// `data-active-section` so CSS can react without extra classes. +// +// "The section the reader is in" is the last section whose top has crossed a +// line a little below the top of the viewport. An anchor jump lands a section +// exactly on that line, so a clicked link is always the one that lights up, +// even for short sections near the end of the page. At the very bottom of the +// page the final section wins, since the reader cannot scroll any further. +// +// Tolerates missing sections and stays idempotent if the initializer runs +// again on the same element. + +const BOUND = "railSpyBound"; +const LINE_PX = 120; + +export function initRailSpy(root: HTMLElement): void { + if (root.dataset[BOUND] === "1") return; + root.dataset[BOUND] = "1"; + + const links = Array.from(root.querySelectorAll(".rail__toc a[href^='#']")); + if (links.length === 0) return; + + const pairs: Array<{ section: HTMLElement; link: HTMLAnchorElement }> = []; + for (const link of links) { + const id = decodeURIComponent(link.getAttribute("href")!.slice(1)); + const section = id ? document.getElementById(id) : null; + if (section == null) { + const row = link.closest(".rail__toc-item") ?? link; + row.style.display = "none"; + continue; + } + pairs.push({ section, link }); + } + if (pairs.length === 0) return; + + const apply = (): void => { + const line = window.scrollY + LINE_PX; + const atBottom = + window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - 2; + + let active: HTMLElement | null = null; + if (atBottom) { + active = pairs[pairs.length - 1]!.section; + } else { + for (const { section } of pairs) { + if (section.offsetTop <= line) active = section; + else break; + } + } + + if (active == null) { + delete root.dataset.activeSection; + } else { + root.dataset.activeSection = active.id; + } + for (const { section, link } of pairs) { + if (section === active) link.setAttribute("aria-current", "true"); + else link.removeAttribute("aria-current"); + } + }; + + let scheduled = false; + const schedule = (): void => { + if (scheduled) return; + scheduled = true; + window.requestAnimationFrame(() => { + scheduled = false; + apply(); + }); + }; + + window.addEventListener("scroll", schedule, { passive: true }); + window.addEventListener("resize", schedule); + window.addEventListener("hashchange", schedule); + apply(); +} diff --git a/apps/marketing/src/content/site-copy.ts b/apps/marketing/src/content/site-copy.ts new file mode 100644 index 0000000000..e7722a604d --- /dev/null +++ b/apps/marketing/src/content/site-copy.ts @@ -0,0 +1,183 @@ +// --------------------------------------------------------------------------- +// Shared marketing copy. One source for text that must read the same in the +// HTML homepage and in the machine-readable Markdown endpoints +// (`/index.md`, `/setup-prompt.md`, `/pricing.md`). +// +// Import from here rather than duplicating strings: the homepage renders the +// human view, the endpoints render the agent view, and both must stay in step. +// --------------------------------------------------------------------------- + +/** + * Copied to the clipboard by the "Set up with your agent" hero CTA, and served + * verbatim at `/setup-prompt.md`. A visitor pastes it into their coding agent + * (Claude, Cursor, ...) and the agent picks the right form of Executor, + * installs it, connects over MCP, and gets a first connection working. Keep in + * sync with the docs "Set up with your agent" section (apps/docs/index.mdx). + */ +export const setupPrompt = `Help me set up Executor and get my first connection working. + +Executor is an open source integration layer for AI agents: one place to configure every integration (MCP servers, OpenAPI specs, GraphQL APIs) and connect to them over MCP. + +Start by helping me pick the right form to run it in. Chat with me about it rather than jumping straight to a yes/no question, and recommend one. If I just want the fastest path, suggest Executor Cloud (free tier, nothing to install). All forms expose the same functionality, just packaged differently: + +Local (everything stays on my machine): +- Desktop app: a desktop app for Mac, Windows, and Linux. Best for a regular desktop environment. +- CLI (\`executor\`): best for a headless or server environment. +Both run a local HTTP server as a background service that any MCP client can connect to. + +Hosted (use it from multiple agents, including cloud ones, with nothing running locally): +- Executor Cloud: hosted, generous free tier, sign in and start immediately. +- Self-hosted: a Docker image. + +How to think about it: +- Want all your data on your own machine? Go local: the desktop app for a regular environment, the CLI for a headless one. +- Want to use it from multiple agents (including cloud agents like ChatGPT), or not run anything locally? Go hosted: Executor Cloud is the fastest start; the self-hosted Docker version gives you full control. + +Terms you'll come across: +- Integration: anything you add (an MCP server, an OpenAPI spec, a GraphQL API). +- Connection: one configured instance of an integration. An integration can have many connections, and a connection doesn't have to be authenticated. +- Policy: whether each tool is always allowed, requires approval, or is blocked. Policies start from a sensible default derived from the imported spec (for example, GET requests on an OpenAPI spec are allowed by default). + +Once you know which form I want: +1. Walk me through installing it. +2. Connect Executor to you over MCP. Most MCP clients only load servers at startup, so after adding it I may need to restart the client or open a new chat before the Executor tools appear. Tell me if that's needed and wait for me to do it before continuing. +3. Once the tools are available, help me add my first integration and get one tool working end to end. + +Docs: https://executor.sh/docs +Source (and the place to start if something breaks): https://github.com/UsefulSoftwareCo/executor`; + +/** Canonical GitHub repository. */ +export const GITHUB_URL = "https://github.com/UsefulSoftwareCo/executor"; + +/** One-line description of the product, used as the Markdown tagline. */ +export const tagline = + "Executor is an MCP gateway. Anything that speaks MCP, like Claude Code, Cursor, or Codex, points at one endpoint and reaches every tool you connect."; + +/** Cache-Control for the Markdown endpoints. Short, so copy edits land fast. */ +export const MARKDOWN_CACHE_CONTROL = "public, max-age=300"; + +/** Content-Type for the Markdown endpoints. */ +export const MARKDOWN_CONTENT_TYPE = "text/markdown; charset=utf-8"; + +export type PricingTier = { + readonly name: string; + readonly price: string; + readonly audience: string; + readonly featuresLabel?: string; + readonly features: ReadonlyArray; + readonly cta: string; +}; + +/** + * Pricing tiers, mirroring the `#pricing` section of the homepage. `/pricing` + * redirects to that anchor, so the homepage is the human source of truth. + */ +export const pricingTiers: ReadonlyArray = [ + { + name: "Free", + price: "$0 / month", + audience: "For small teams getting started", + features: ["Up to 3 members", "100,000 executions per month", "Unlimited integrations"], + cta: "Start free: https://executor.sh/cloud", + }, + { + name: "Team", + price: "$15 / member / month", + audience: "For growing organizations (recommended)", + features: [ + "14-day free trial, then $15 / member / month", + "Unlimited executions", + "Verified domains & join by team domain", + ], + cta: "Start free trial: https://executor.sh/cloud", + }, + { + name: "Enterprise", + price: "Custom", + audience: "For orgs with custom needs", + featuresLabel: "Everything in Team, plus", + features: [ + "Self-hosted or dedicated cloud deployment support", + "SSO / SAML & SCIM provisioning", + "Audit logs for every tool call", + "Dedicated support & onboarding", + "Security reviews, DPA & SOC 2 on request", + ], + cta: "Contact rhys@executor.sh", + }, +]; + +export type Capability = { + readonly title: string; + readonly body: string; + readonly comingSoon?: boolean; +}; + +/** The six capability cards from the homepage "What you get" section. */ +export const capabilities: ReadonlyArray = [ + { + title: "One tool shape", + body: "MCP, OpenAPI, GraphQL, or a custom integration. Under the hood they all become a tool name, an input schema, and an output schema.", + }, + { + title: "Call it any way", + body: "Today it is a code-mode MCP. It could just as well be the Executor CLI, a one-off script, a gen-UI dashboard, or a reusable workflow. Same tools, every surface.", + }, + { + title: "Trace every call", + body: "One place to see every run and tool call. Audit any decision after the fact.", + comingSoon: true, + }, + { + title: "Set up once, whole team has it", + body: "Per-user credentials and shared ones. No onboarding ritual, no toggling MCPs on and off mid-task.", + }, + { + title: "Destructive actions pull you back in", + body: "Executor keeps the semantics it imported: GET vs DELETE for OpenAPI, destructiveHint for MCP, mutations for GraphQL. Agents auto-run the safe stuff and ask before the rest.", + }, + { + title: "Sandboxed execution", + body: "Tool calls run in an isolated JavaScript sandbox. Secrets are injected host-side at call time and never enter the sandbox heap, so the agent and model never see a raw token.", + }, +]; + +export type Faq = { readonly question: string; readonly answer: string }; + +/** The homepage FAQ. */ +export const faqs: ReadonlyArray = [ + { + question: "Where does my code run, and what touches my credentials?", + answer: + "Tool calls run in an isolated JavaScript sandbox. Credentials are resolved host-side at call time and injected into the outbound request only. They never enter the sandbox heap, the code your agent wrote, the agent, or the model.", + }, + { + question: "Can the agent or the model ever see a raw token?", + answer: + "No. Secrets stay host-side by design. The sandbox calls a tool by name; Executor attaches the credential to the real request outside the sandbox, so a token is never present in anything the model can read.", + }, + { + question: "What can call Executor?", + answer: + "Any MCP client (Claude Code, Cursor, Codex, and others), the Executor CLI, or a native client you drop in. Because tools share one shape, the calling surface is interchangeable.", + }, + { + question: "How does it know what is safe to auto-run?", + answer: + "Executor preserves the semantics of whatever it imported: GET vs DELETE for OpenAPI, destructiveHint for MCP, and mutations for GraphQL. That tells the agent what it can run on its own and what should pull you back into the loop.", + }, + { + question: "Is it open source? Can I self-host?", + answer: + "Yes. Executor is open source and built on the SDK we publish to npm. Run the desktop app locally, self-host the server, or use the hosted cloud. Same code paths, different deployment.", + }, +]; + +/** Response helper shared by the Markdown endpoints. */ +export const markdownResponse = (body: string): Response => + new Response(body, { + headers: { + "Content-Type": MARKDOWN_CONTENT_TYPE, + "Cache-Control": MARKDOWN_CACHE_CONTROL, + }, + }); diff --git a/apps/marketing/src/pages/index.astro b/apps/marketing/src/pages/index.astro index 2184b4ec12..6000f5db44 100644 --- a/apps/marketing/src/pages/index.astro +++ b/apps/marketing/src/pages/index.astro @@ -1,11 +1,11 @@ --- import Layout from "../layouts/Layout.astro"; -import { AnimatedBeamDemo } from "../components/animated-beam-demo"; -import { ContextBloatDemo } from "../components/context-bloat-demo"; // Imported so Vite emits it as a hashed build asset (served from /_astro/...); // the deploy pipeline does not pick up newly added /public files. -import ycBackedBy from "../assets/yc-backed-by.svg?url"; import { getStars, formatStars } from "../lib/github"; +import { setupPrompt } from "../content/site-copy"; +import RailB from "../components/variants/rail-b.astro"; +import DocBody from "../components/variants/doc-body.astro"; const GH = "https://github.com/UsefulSoftwareCo/executor"; // Cloud sign-in. Same origin in prod: the cloud Worker owns executor.sh and @@ -14,42 +14,6 @@ const GH = "https://github.com/UsefulSoftwareCo/executor"; const SIGN_IN = "/api/auth/login"; const stars = await getStars(); -// Copied to the clipboard by the "Set up with your agent" hero CTA. A visitor -// pastes it into their coding agent (Claude, Cursor, ...) and the agent picks -// the right form of Executor, installs it, connects over MCP, and gets a first -// connection working. Keep in sync with the docs "Set up with your agent" -// section (apps/docs/index.mdx). -const setupPrompt = `Help me set up Executor and get my first connection working. - -Executor is an open source integration layer for AI agents: one place to configure every integration (MCP servers, OpenAPI specs, GraphQL APIs) and connect to them over MCP. - -Start by helping me pick the right form to run it in. Chat with me about it rather than jumping straight to a yes/no question, and recommend one. If I just want the fastest path, suggest Executor Cloud (free tier, nothing to install). All forms expose the same functionality, just packaged differently: - -Local (everything stays on my machine): -- Desktop app: a native app for Mac, Windows, and Linux. Best for a regular desktop environment. -- CLI (\`executor\`): best for a headless or server environment. -Both run a local HTTP server as a background service that any MCP client can connect to. - -Hosted (use it from multiple agents, including cloud ones, with nothing running locally): -- Executor Cloud: hosted, generous free tier, sign in and start immediately. -- Self-hosted: a Docker image or a Cloudflare Worker. - -How to think about it: -- Want all your data on your own machine? Go local: the desktop app for a regular environment, the CLI for a headless one. -- Want to use it from multiple agents (including cloud agents like ChatGPT), or not run anything locally? Go hosted: Executor Cloud is the fastest start; the self-hosted Docker or Cloudflare versions give you full control. - -Terms you'll come across: -- Integration: anything you add (an MCP server, an OpenAPI spec, a GraphQL API). -- Connection: one configured instance of an integration. An integration can have many connections, and a connection doesn't have to be authenticated. -- Policy: whether each tool is always allowed, requires approval, or is blocked. Policies start from a sensible default derived from the imported spec (for example, GET requests on an OpenAPI spec are allowed by default). - -Once you know which form I want: -1. Walk me through installing it. -2. Connect Executor to you over MCP. Most MCP clients only load servers at startup, so after adding it I may need to restart the client or open a new chat before the Executor tools appear. Tell me if that's needed and wait for me to do it before continuing. -3. Once the tools are available, help me add my first integration and get one tool working end to end. - -Docs: https://executor.sh/docs -Source (and the place to start if something breaks): https://github.com/UsefulSoftwareCo/executor`; --- @@ -66,688 +30,11 @@ Source (and the place to start if something breaks): https://github.com/UsefulSo > -
- {/* ─── NAV ─── */} - - - {/* ─── HERO ─── */} -
- -
-
-

- Connect any agent to everything. -

-

- Executor is an MCP gateway. Anything that speaks MCP, like Claude - Code, Cursor, or Codex, points at one endpoint and reaches every - tool you connect. -

-
-
- -
-
- {/* ── CTA: For humans / For agents ── */} -
-
- - - -
- - {/* For humans: sign up or run it yourself */} - - - {/* For agents: hand setup to a coding agent, or read the docs */} -
- - Read docs -
-
- - - {/* Shadow lives here, not in the SVG: WebKit rasterizes SVG - filter regions in at 1x, blurring the badge on mobile. */} - Backed by Y Combinator - -
-
-
- - {/* ─── WEDGE ─── */} -
-
-

- Wiring tools to agents is fiddly, per-client, and easy to get wrong. - Executor makes every tool, from any protocol, look the - same: one name, one input - schema, one output schema, so any agent can call any of them - the same way. -

-
-
- - {/* ─── NO CONTEXT BLOAT (interactive) ─── */} -
-
-
-
Context efficiency
-

- Thousands of tools, no bloat. -

-

- Connect everything you use and Executor still shows the model a - single tool. It searches your catalog and loads a tool's schema - only when the code actually calls it, so the prompt never - balloons. -

-
- - -
-
- - {/* ─── CAPABILITIES ─── */} -
-
-
-
What you get
-

- The model reasons. Executor handles the - rest. -

-
- -
- {/* One tool shape */} -
- 01 -

One tool shape

-

- MCP, OpenAPI, GraphQL, or a custom integration. Under the hood - they all become a tool name, an input schema, and an output - schema. -

-
- - {/* Code-mode */} -
- 02 -

Call it any way

-

- Today it is a code-mode MCP. It could just as well be the - Executor CLI, a one-off script, a gen-UI dashboard, or a reusable - workflow. Same tools, every surface. -

-
- - {/* Trace every call (coming soon) */} -
- Coming soon - 03 -

Trace every call

-

- One place to see every run and tool call. Audit any - decision after the fact. -

-
-
- run_7421 - - 1.42s -
-
- sentry.getIssue - - 184ms -
-
- github.searchCode - - 391ms -
-
- linear.createIssue - - 612ms -
-
-
- - {/* Teams */} -
- 04 -

Set up once, whole team has it

-

- Per-user credentials and shared ones. No onboarding ritual, no - toggling MCPs on and off mid-task. -

-
- - {/* Destructive gating */} -
- 05 -

Destructive actions pull you back in

-

- Executor keeps the semantics it imported: GET vs DELETE for - OpenAPI, destructiveHint for MCP, mutations for GraphQL. Agents - auto-run the safe stuff and ask before the rest. -

-
- - {/* Sandboxed */} -
- 06 -

Sandboxed execution

-

- Tool calls run in an isolated JavaScript sandbox. Secrets are - injected host-side at call time and never enter the sandbox - heap, so the agent and model never see a raw token. -

-
-
-
-
- - {/* ─── FOUNDER NOTE (condensed) ─── */} -
-
-
Why we built it
-
-

- Your agent should be able to reach your company's resources in a - way that isn't scary. Most setups make you choose between locked - down and useless, or wide open and risky. -

-

- Executor doesn't care what you add. Once a tool is in that one - shape, a name and two schemas, you can call it however you want and - the same guardrails apply everywhere. That is the whole idea: make - the safe path the easy path. -

-
-
-
- - {/* ─── CHOOSE YOUR PATH ─── */} -
-
-
-
Get started
-

- Pick your path. -

-
- -
- - - - - - {/* Cloud (recommended: fastest start) */} -
-

- Cloud -

- executor.sh/cloud -

- Hosted Executor. Auth, sync, policies, and your whole team online - in five minutes. Free tier to start. -

- Try Cloud → -

- Looking for self-hosted? → -

-
- - {/* Desktop (native app, local) */} -
-

- Desktop -

- Mac · Windows · Linux -

- A native app that runs entirely on your machine. Your - integrations, credentials, and sessions never leave the device. - MIT licensed. -

- -
- - {/* CLI (headless / servers) */} -
-

- CLI -

- npm i -g executor -

- Run Executor as a background service and drive it from your - terminal. Best for headless and server environments. MIT - licensed. -

- Read the docs → -

- View source → -

-
-
-
-
- - {/* ─── FAQ ─── */} -
-
-
-

FAQ

-
- -
-
- - Where does my code run, and what touches my credentials? - - -
- Tool calls run in an isolated JavaScript sandbox. Credentials are - resolved host-side at call time and injected into the outbound - request only. They never enter the sandbox heap, the code your - agent wrote, the agent, or the model. -
-
- -
- - Can the agent or the model ever see a raw token? - - -
- No. Secrets stay host-side by design. The sandbox calls a tool by - name; Executor attaches the credential to the real request - outside the sandbox, so a token is never present in anything the - model can read. -
-
+
+
+
-
- - What can call Executor? - - -
- Any MCP client (Claude Code, Cursor, Codex, and others), the - Executor CLI, or a native client you drop in. Because tools share - one shape, the calling surface is interchangeable. -
-
- -
- - How does it know what is safe to auto-run? - - -
- Executor preserves the semantics of whatever it imported: GET vs - DELETE for OpenAPI, destructiveHint for MCP, and mutations for - GraphQL. That tells the agent what it can run on its own and what - should pull you back into the loop. -
-
- -
- - Is it open source? Can I self-host? - - -
- Yes. Executor is MIT licensed and built on the SDK we publish to - npm. Run the desktop app locally, self-host the server, or use - the hosted cloud. Same code paths, different deployment. -
-
-
-
-
- - {/* ─── PRICING ─── */} -
-
-
-
Pricing
-

- Start free, pay per member. -

-
- -
- {/* Free */} -
-
- Get started -
-

- Free -

-

For small teams getting started

-
- $0 - / month -
- Start free → -
    - {[ - "Up to 3 members", - "100,000 executions per month", - "Unlimited integrations", - ].map((f) => ( -
  • - {f} -
  • - ))} -
-
- - {/* Team */} -
-
- Recommended -
-

- Team -

-

For growing organizations

-
- $15 - / member / month -
- Start free trial → -
    - {[ - "14-day free trial, then $15 / member / month", - "Unlimited executions", - "Verified domains & join by team domain", - ].map((f) => ( -
  • - {f} -
  • - ))} -
-
- - {/* Enterprise */} -
-
- Custom needs -
-

- Enterprise -

-

For orgs with custom needs

-
- Custom -
- Contact us → -

Everything in Team, plus

-
    - {[ - "Self-hosted or dedicated cloud deployment support", - "SSO / SAML & SCIM provisioning", - "Audit logs for every tool call", - "Dedicated support & onboarding", - "Security reviews, DPA & SOC 2 on request", - ].map((f) => ( -
  • - {f} -
  • - ))} -
-
-
-
-
- - {/* ─── FINAL CTA ─── */} -
-
-

- Connect your agent to - everything. -

- -

- or try Executor Cloud → -

-
-
+ {/* ─── FOOTER ─── */}
@@ -772,6 +59,7 @@ Source (and the place to start if something breaks): https://github.com/UsefulSo
+
@@ -828,115 +116,6 @@ Source (and the place to start if something breaks): https://github.com/UsefulSo } - - - -