From 8aaf41e1f226f8add43a13c1b76b89b4de7e2ed5 Mon Sep 17 00:00:00 2001 From: Rhys Sullivan <39114868+RhysSullivan@users.noreply.github.com> Date: Thu, 27 Aug 2026 15:03:37 -0700 Subject: [PATCH 1/5] Add research page on MCP tool presentation across harnesses --- .../pages/research/mcp-tool-visibility.astro | 598 ++++++++++++++++++ 1 file changed, 598 insertions(+) create mode 100644 apps/marketing/src/pages/research/mcp-tool-visibility.astro diff --git a/apps/marketing/src/pages/research/mcp-tool-visibility.astro b/apps/marketing/src/pages/research/mcp-tool-visibility.astro new file mode 100644 index 000000000..66f57c277 --- /dev/null +++ b/apps/marketing/src/pages/research/mcp-tool-visibility.astro @@ -0,0 +1,598 @@ +--- +import Layout from "../../layouts/Layout.astro"; + +const GH = "https://github.com/UsefulSoftwareCo/executor"; +const title = "How agents actually see your MCP tools"; +const description = + "The same MCP server was connected to five coding agents and the model-bound traffic was measured. Each harness shows the model something different: full definitions, bare names, or nothing at all."; + +// Δ prompt tokens for the 31 per-integration search tools, provider-reported. +const costs = [ + { name: "Claude Code", detail: "2.1.247 · inline", delta: 3464, plain: "36,013", search: "39,477" }, + { name: "opencode (beta)", detail: "default · inline", delta: 3400, plain: "22,118", search: "25,518" }, + { name: "opencode", detail: "1.18.15 · inline", delta: 3399, plain: "17,458", search: "20,857" }, + { name: "opencode (beta)", detail: "code mode · TS signatures", delta: 1280, plain: "16,126", search: "17,406" }, + { name: "Cursor CLI", detail: "2026.07.23 · names only", delta: 170, plain: "16,140", search: "16,310" }, + { name: "Codex CLI", detail: "0.147.0 · code mode", delta: 0, plain: "15,776", search: "15,776" }, +]; +const max = Math.max(...costs.map((c) => c.delta)); +--- + + +
+
+
+
+
+ +
+ + + {/* ─── HEADER ─── */} +
+
+ Research · August 2026 +

+ {title} +

+

+ {description} We connected one Executor workspace — 31 integrations — to Claude Code, + Cursor's CLI, Codex, and two versions of opencode, twice each: once plain, once with + Executor's ?search_tools=true option, which adds one + minimal search_<integration> tool per connected + integration. The difference between those two sessions is exactly what 31 extra tool + definitions cost, and where they end up. +

+
+
+ + {/* ─── THE THREE REALITIES ─── */} +
+
+ What each harness shows the model +

Five harnesses, three different realities

+

+ "The model sees your tools" hides three different mechanisms. Every card below is one + harness's view of the same server, drawn from the captured request that actually went + to the model. Solid blocks are in the context window on every turn; dashed blocks are + fetched on demand; crossed-out blocks never reach the model at all. +

+ +
+ {/* Inliners */} +
+
+
+ Claude Code · opencode + family: inline — every definition, every turn +
+ +3,399–3,464 tokens +
+
+
+
the model's context window
+
system prompt
+
+ 38 tool definitions — full JSON schema each + name + description + parameters for every tool, inlined as API function tools +
+
conversation
+
+
+
+
+ + tools[7] of 38 — as sent to the model +
+
{`{
+  "name": "mcp__executor__search_mcp_linear_app",
+  "description": "Search this integration's tools;
+    empty query lists all. Run results with execute.",
+  "input_schema": {
+    "type": "object",
+    "properties": { "query": { "type": "string" } }
+  }
+}`}
+
+

+ To search Linear’s tools + one call — search_mcp_linear_app({"{ query }"}), + the definition is already in context. +

+
+
+
+ + {/* opencode code mode */} +
+
+
+ opencode · code mode + family: code mode, transparent — tools become typed signatures +
+ +1,280 tokens +
+
+
+
the model's context window
+
system prompt
+
13 built-in tools
+
+ one execute tool + its description lists every MCP tool as a TypeScript signature — 2.7× denser than JSON definitions +
+
conversation
+
+
+
+
+ + inside execute's description +
+
{`- tools.executor.search_google_gmail(input: {
+    query?: string,
+  }): Promise // Search this integration's
+    tools; empty query lists all. Run results
+    with execute.`}
+
+

+ To search Linear’s tools + one call through code — + execute("await tools.executor.search_mcp_linear_app(…)"). +

+
+
+
+ + {/* Cursor */} +
+
+
+ Cursor CLI + family: virtualized — a names-only catalog +
+ +170 tokens +
+
+
+
the model's context window
+
system prompt
+
19 first-party tools, incl. GetDynamicTools / CallDynamicTool
+
+ catalog: bare tool names, grouped by server + ≈5.5 tokens per tool — the name is all the model gets +
+
+ descriptions & schemas — fetched on demand via GetDynamicTools +
+
conversation
+
+
+
+
+ + the model's catalog view +
+
{`executor: execute · skills · resume ·
+  search_axiom_mcp · search_github_rest ·
+  search_google_gmail · search_mcp_linear_app · …
+
+(full definition available via GetDynamicTools —
+ not fetched unless the model asks)`}
+
+

+ To search Linear’s tools + the name is visible; call it through + CallDynamicTool("search_mcp_linear_app", …), + optionally fetching the schema first. +

+
+
+
+ + {/* Codex */} +
+
+
+ Codex CLI + family: code mode, opaque — nothing reaches the prompt +
+ +0 tokens +
+
+
+
the model's context window
+
system prompt
+
9 native tools — exec, collaboration, …
+
+ your MCP tools + carried only in request metadata the model never reads; prompts were byte-identical with 7 vs 38 tools +
+
conversation
+
+
+
+
+ + request metadata — not model-visible +
+
{`"code_mode_tool_names": {
+  "search_google_gmail":
+    { "namespace": "mcp__executor" },
+  … 37 more
+}`}
+
+

+ To search Linear’s tools + the model must call exec and write code — + await tools.mcp__executor__search_mcp_linear_app(…) — + discovering what exists only from inside the sandbox. +

+
+
+
+
+
+
+ + {/* ─── COST ─── */} +
+
+ Measured, not estimated +

What 31 tool definitions cost, by harness

+

+ Δ prompt tokens between the plain and ?search_tools=true sessions, + as reported by the provider's usage accounting (input + cache write + cache read). Same + workspace, same prompt; only the endpoint query differs. +

+
+ { + costs.map((c) => ( +
+
+ {c.name} + {c.detail} +
+
+
+
+
{c.delta === 0 ? "0" : `+${c.delta.toLocaleString()}`}
+
+ )) + } +
+

+ Cursor's model traffic terminates at its backend, so its pair comes from Cursor's own + per-request usage meter rather than a captured prompt. JSON tool definitions tokenize at + roughly 2.4 characters per token — schema punctuation is token-dense. +

+
+
+ + {/* ─── TAKEAWAYS ─── */} +
+
+ If you ship an MCP server +

Design for the weakest channel

+
+
+ 01 +

The name is the only universal channel

+

+ Every harness that shows tools at all shows the name. Descriptions reach only the + inliners; schemas effectively reach only the inliners. If information must arrive, + put it in the name. +

+
+
+ 02 +

Schema tricks don't travel

+

+ An enum of options inside a parameter looks clever and costs less — and is invisible + in Cursor and Codex, the two harnesses where you'd need it most. +

+
+
+ 03 +

Keep definitions small anyway

+

+ Where tools are inlined, every byte is paid on every turn, multiplied by tool count. + One shared description sentence and a single parameter cut this surface by more than half. +

+
+
+ 04 +

Measure with provider usage, not estimates

+

+ Client-side token meters and character counts both mislead. Run the same session + twice and diff what the provider bills — that number is the truth. +

+
+
+
+
+ + {/* ─── BOTTOM CTA ─── */} +
+
+

+ Connect any agent to everything. +

+ +
+
+ + {/* ─── FOOTER ─── */} + +
+
+
+ + From beab34d810e3efd29195483147be23c9517830a4 Mon Sep 17 00:00:00 2001 From: Rhys Sullivan <39114868+RhysSullivan@users.noreply.github.com> Date: Thu, 27 Aug 2026 15:37:09 -0700 Subject: [PATCH 2/5] Use harness brand marks on the research page --- apps/marketing/src/assets/logos/opencode.svg | 1 + .../pages/research/mcp-tool-visibility.astro | 94 +++++++++++++++---- 2 files changed, 75 insertions(+), 20 deletions(-) create mode 100644 apps/marketing/src/assets/logos/opencode.svg diff --git a/apps/marketing/src/assets/logos/opencode.svg b/apps/marketing/src/assets/logos/opencode.svg new file mode 100644 index 000000000..16327e94a --- /dev/null +++ b/apps/marketing/src/assets/logos/opencode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/marketing/src/pages/research/mcp-tool-visibility.astro b/apps/marketing/src/pages/research/mcp-tool-visibility.astro index 66f57c277..bf5d6aafa 100644 --- a/apps/marketing/src/pages/research/mcp-tool-visibility.astro +++ b/apps/marketing/src/pages/research/mcp-tool-visibility.astro @@ -1,5 +1,9 @@ --- import Layout from "../../layouts/Layout.astro"; +import claudeLogo from "../../assets/logos/claude.svg?url"; +import codexLogo from "../../assets/logos/codex.svg?url"; +import cursorLogo from "../../assets/logos/cursor.svg?url"; +import opencodeLogo from "../../assets/logos/opencode.svg?url"; const GH = "https://github.com/UsefulSoftwareCo/executor"; const title = "How agents actually see your MCP tools"; @@ -8,12 +12,12 @@ const description = // Δ prompt tokens for the 31 per-integration search tools, provider-reported. const costs = [ - { name: "Claude Code", detail: "2.1.247 · inline", delta: 3464, plain: "36,013", search: "39,477" }, - { name: "opencode (beta)", detail: "default · inline", delta: 3400, plain: "22,118", search: "25,518" }, - { name: "opencode", detail: "1.18.15 · inline", delta: 3399, plain: "17,458", search: "20,857" }, - { name: "opencode (beta)", detail: "code mode · TS signatures", delta: 1280, plain: "16,126", search: "17,406" }, - { name: "Cursor CLI", detail: "2026.07.23 · names only", delta: 170, plain: "16,140", search: "16,310" }, - { name: "Codex CLI", detail: "0.147.0 · code mode", delta: 0, plain: "15,776", search: "15,776" }, + { name: "Claude Code", icon: claudeLogo, detail: "2.1.247 · inline", delta: 3464, plain: "36,013", search: "39,477" }, + { name: "opencode (beta)", icon: opencodeLogo, detail: "default · inline", delta: 3400, plain: "22,118", search: "25,518" }, + { name: "opencode", icon: opencodeLogo, detail: "1.18.15 · inline", delta: 3399, plain: "17,458", search: "20,857" }, + { name: "opencode (beta)", icon: opencodeLogo, detail: "code mode · TS signatures", delta: 1280, plain: "16,126", search: "17,406" }, + { name: "Cursor CLI", icon: cursorLogo, detail: "2026.07.23 · names only", delta: 170, plain: "16,140", search: "16,310" }, + { name: "Codex CLI", icon: codexLogo, detail: "0.147.0 · code mode", delta: 0, plain: "15,776", search: "15,776" }, ]; const max = Math.max(...costs.map((c) => c.delta)); --- @@ -85,9 +89,15 @@ const max = Math.max(...costs.map((c) => c.delta)); {/* Inliners */}
-
- Claude Code · opencode - family: inline — every definition, every turn +
+ + Claude + opencode + +
+ Claude Code · opencode + family: inline — every definition, every turn +
+3,399–3,464 tokens
@@ -130,9 +140,14 @@ const max = Math.max(...costs.map((c) => c.delta)); {/* opencode code mode */}
-
- opencode · code mode - family: code mode, transparent — tools become typed signatures +
+ + opencode + +
+ opencode · code mode + family: code mode, transparent — tools become typed signatures +
+1,280 tokens
@@ -172,9 +187,14 @@ const max = Math.max(...costs.map((c) => c.delta)); {/* Cursor */}
-
- Cursor CLI - family: virtualized — a names-only catalog +
+ + Cursor + +
+ Cursor CLI + family: virtualized — a names-only catalog +
+170 tokens
@@ -219,9 +239,14 @@ const max = Math.max(...costs.map((c) => c.delta)); {/* Codex */}
-
- Codex CLI - family: code mode, opaque — nothing reaches the prompt +
+ + Codex + +
+ Codex CLI + family: code mode, opaque — nothing reaches the prompt +
+0 tokens
@@ -277,8 +302,11 @@ const max = Math.max(...costs.map((c) => c.delta)); costs.map((c) => (
- {c.name} - {c.detail} + + + {c.name} + {c.detail} +
c.delta)); color: var(--color-ink); white-space: nowrap; } + .persp-id { + display: flex; + align-items: center; + gap: 12px; + min-width: 0; + } + .persp-icons { + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; + } + .persp-icons img { + display: block; + height: 20px; + width: 20px; + } .persp-body { display: grid; grid-template-columns: 340px 1fr; @@ -559,10 +604,19 @@ const max = Math.max(...costs.map((c) => c.delta)); } } .cost-name { + display: flex; + align-items: center; + gap: 10px; font-size: 14px; font-weight: 500; color: var(--color-ink); } + .cost-icon { + display: block; + height: 16px; + width: 16px; + flex-shrink: 0; + } .cost-detail { display: block; font-family: var(--font-mono); From d0646923fbda08d113af0e5c097b041e83ccdb28 Mon Sep 17 00:00:00 2001 From: Rhys Sullivan <39114868+RhysSullivan@users.noreply.github.com> Date: Thu, 27 Aug 2026 15:47:39 -0700 Subject: [PATCH 3/5] Frame each harness card as its own window --- .../pages/research/mcp-tool-visibility.astro | 162 ++++++++++-------- 1 file changed, 95 insertions(+), 67 deletions(-) diff --git a/apps/marketing/src/pages/research/mcp-tool-visibility.astro b/apps/marketing/src/pages/research/mcp-tool-visibility.astro index bf5d6aafa..86e601ab6 100644 --- a/apps/marketing/src/pages/research/mcp-tool-visibility.astro +++ b/apps/marketing/src/pages/research/mcp-tool-visibility.astro @@ -88,19 +88,20 @@ const max = Math.max(...costs.map((c) => c.delta));
{/* Inliners */}
-
-
- - Claude - opencode - -
- Claude Code · opencode - family: inline — every definition, every turn -
-
+
+ + + Claude + opencode + + Claude Code · opencode + 2.1.247 · 1.18.15 +3,399–3,464 tokens
+

+ Every definition is pasted into the prompt, every turn — name, description, and + schema for all 38 tools. +

the model's context window
@@ -139,18 +140,19 @@ const max = Math.max(...costs.map((c) => c.delta)); {/* opencode code mode */}
-
-
- - opencode - -
- opencode · code mode - family: code mode, transparent — tools become typed signatures -
-
+
+ + + opencode + + opencode · code mode + OPENCODE_EXPERIMENTAL_CODE_MODE +1,280 tokens
+

+ Your tools collapse into one execute tool and + reappear inside its description as typed signatures — still readable, 2.7× denser. +

the model's context window
@@ -186,18 +188,19 @@ const max = Math.max(...costs.map((c) => c.delta)); {/* Cursor */}
-
-
- - Cursor - -
- Cursor CLI - family: virtualized — a names-only catalog -
-
+
+ + + Cursor + + Cursor CLI + 2026.07.23 +170 tokens
+

+ The model gets a names-only catalog. Descriptions and schemas exist on Cursor's + side, but the model has to ask for them. +

the model's context window
@@ -238,18 +241,19 @@ const max = Math.max(...costs.map((c) => c.delta)); {/* Codex */}
-
-
- - Codex - -
- Codex CLI - family: code mode, opaque — nothing reaches the prompt -
-
+
+ + + Codex + + Codex CLI + 0.147.0 +0 tokens
+

+ Nothing about your tools reaches the prompt. They exist only as callables inside + Codex's code sandbox — the model discovers them by writing code. +

the model's context window
@@ -422,51 +426,75 @@ const max = Math.max(...costs.map((c) => c.delta)); padding: 0; overflow: hidden; } - .persp-head { + /* window titlebar — the card IS the harness's window */ + .persp-bar { display: flex; - align-items: baseline; - justify-content: space-between; - gap: 16px; - padding: 18px 24px; + align-items: center; + gap: 10px; + padding: 12px 18px; border-bottom: 1px solid var(--color-rule); + background: var(--color-surface-2); + } + .persp-dot { + width: 9px; + height: 9px; + border-radius: 9999px; + background: var(--color-rule-strong); + flex-shrink: 0; + } + .persp-dot + .persp-dot { + margin-left: -3px; + } + .persp-icons { + display: flex; + align-items: center; + gap: 7px; + flex-shrink: 0; + margin-left: 8px; + } + .persp-icons img { + display: block; + height: 18px; + width: 18px; } .persp-name { font-weight: 600; - font-size: 17px; + font-size: 15px; letter-spacing: -0.01em; color: var(--color-ink); + white-space: nowrap; } - .persp-family { - display: block; - margin-top: 3px; + .persp-ver { font-family: var(--font-mono); font-size: 11px; - letter-spacing: 0.08em; - text-transform: uppercase; color: var(--color-ink-3); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 0; } .persp-cost { font-family: var(--font-mono); - font-size: 13px; + font-size: 12.5px; color: var(--color-ink); white-space: nowrap; + margin-left: auto; } - .persp-id { - display: flex; - align-items: center; - gap: 12px; - min-width: 0; + @media (max-width: 640px) { + .persp-ver { + display: none; + } } - .persp-icons { - display: flex; - align-items: center; - gap: 8px; - flex-shrink: 0; + .persp-lede { + padding: 16px 24px 0; + font-size: 14.5px; + line-height: 1.6; + color: var(--color-ink-2); + max-width: 72ch; } - .persp-icons img { - display: block; - height: 20px; - width: 20px; + /* one window chrome per card: inner excerpts keep a filename bar, no dots */ + .persp-detail .code-dot { + display: none; } .persp-body { display: grid; From cd450ba67ff43633756bfe9aa73d3fa5647db3b7 Mon Sep 17 00:00:00 2001 From: Rhys Sullivan <39114868+RhysSullivan@users.noreply.github.com> Date: Thu, 27 Aug 2026 16:09:46 -0700 Subject: [PATCH 4/5] Rework research page around a tabbed context-window viewer --- .../pages/research/mcp-tool-visibility.astro | 588 ++++++++---------- 1 file changed, 267 insertions(+), 321 deletions(-) diff --git a/apps/marketing/src/pages/research/mcp-tool-visibility.astro b/apps/marketing/src/pages/research/mcp-tool-visibility.astro index 86e601ab6..d69834a75 100644 --- a/apps/marketing/src/pages/research/mcp-tool-visibility.astro +++ b/apps/marketing/src/pages/research/mcp-tool-visibility.astro @@ -8,7 +8,7 @@ import opencodeLogo from "../../assets/logos/opencode.svg?url"; const GH = "https://github.com/UsefulSoftwareCo/executor"; const title = "How agents actually see your MCP tools"; const description = - "The same MCP server was connected to five coding agents and the model-bound traffic was measured. Each harness shows the model something different: full definitions, bare names, or nothing at all."; + "The same MCP server, connected to five coding agents, with the model-bound traffic measured. Each harness shows the model something different: full definitions, bare names, or nothing at all."; // Δ prompt tokens for the 31 per-integration search tools, provider-reported. const costs = [ @@ -63,229 +63,141 @@ const max = Math.max(...costs.map((c) => c.delta)); {title}

- {description} We connected one Executor workspace — 31 integrations — to Claude Code, - Cursor's CLI, Codex, and two versions of opencode, twice each: once plain, once with - Executor's ?search_tools=true option, which adds one - minimal search_<integration> tool per connected - integration. The difference between those two sessions is exactly what 31 extra tool - definitions cost, and where they end up. + One Executor workspace — 31 integrations — connected to each harness twice: plain, and + with ?search_tools=true, which adds one minimal + search_<integration> tool per integration. The + difference between the two captured sessions is what 31 tool definitions cost, and + where they end up.

- {/* ─── THE THREE REALITIES ─── */} + {/* ─── CONTEXT VIEWER ─── */}
- What each harness shows the model -

Five harnesses, three different realities

-

- "The model sees your tools" hides three different mechanisms. Every card below is one - harness's view of the same server, drawn from the captured request that actually went - to the model. Solid blocks are in the context window on every turn; dashed blocks are - fetched on demand; crossed-out blocks never reach the model at all. -

+ The model's context window, per harness +

Same server, four context windows

-
- {/* Inliners */} -
-
- - - Claude - opencode - - Claude Code · opencode - 2.1.247 · 1.18.15 - +3,399–3,464 tokens +
+
+ + + + +
+ +
+ v2.1.247 + + + + -

- Every definition is pasted into the prompt, every turn — name, description, and - schema for all 38 tools. -

-
-
-
the model's context window
-
system prompt
-
- 38 tool definitions — full JSON schema each - name + description + parameters for every tool, inlined as API function tools -
-
conversation
-
-
-
-
- - tools[7] of 38 — as sent to the model -
-
{`{
+              +3,464 tokens for the 31 tools
+            
+ + {/* Claude Code */} +
+
system prompt
+
built-in tools 12 — Bash, Read, Edit, …
+
+ your MCP tools · 38 of 38 in context +
{`{
   "name": "mcp__executor__search_mcp_linear_app",
-  "description": "Search this integration's tools;
-    empty query lists all. Run results with execute.",
-  "input_schema": {
-    "type": "object",
-    "properties": { "query": { "type": "string" } }
-  }
+  "description": "Search this integration's tools; empty query lists all.
+    Run results with execute.",
+  "input_schema": { "type": "object",
+    "properties": { "query": { "type": "string" } } }
 }`}
-
-

- To search Linear’s tools - one call — search_mcp_linear_app({"{ query }"}), - the definition is already in context. -

-
+ full name + description + schema, × 38, resent every turn
+
conversation
+
prompt 36,013 → 39,477 tokens · Δ +3,464
- {/* opencode code mode */} -
-
- - - opencode - - opencode · code mode - OPENCODE_EXPERIMENTAL_CODE_MODE - +1,280 tokens + {/* opencode, code mode off */} +