Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
b6df51d
Render integration capability descriptions in the execute inventory
justcarlson Sep 26, 2026
7fffabf
Format description.test.ts with oxfmt
justcarlson Sep 26, 2026
b796660
fix: deliver tool attachments automatically through MCP
justcarlson Oct 3, 2026
c377b09
fix: omit serialized attachment bytes from result previews
justcarlson Oct 3, 2026
6aac1a5
Merge pull request #1 from justcarlson/native-attachments
justcarlson Oct 3, 2026
3208bee
perf: resolve tool policies once per snapshot for fast MCP search
justcarlson Oct 8, 2026
06d9a13
Merge pull request #2 from justcarlson/perf/search-policy-cache
justcarlson Oct 8, 2026
1973943
perf: refresh self-host tool catalogs without delaying reads
justcarlson Oct 8, 2026
cf84e9e
perf(mcp): return compact search hits by default
justcarlson Oct 8, 2026
4f9d581
perf(mcp): name nested object keys in compact argument summaries
justcarlson Oct 8, 2026
f5eabcd
feat(mcp): accept integration aliases and rank by display name
justcarlson Oct 8, 2026
f57f61e
docs(mcp): steer passthrough agents to the three-call path
justcarlson Oct 8, 2026
5e06f2f
Merge pull request #5 from justcarlson/perf/cold-search-background
justcarlson Oct 8, 2026
bb614f8
Merge pull request #6 from justcarlson/perf/compact-search
justcarlson Oct 8, 2026
1044fe5
Merge remote-tracking branch 'origin/inventory-descriptions' into HEAD
justcarlson Oct 8, 2026
e6ba795
Merge pull request #13 from justcarlson/perf/integration-aliases
justcarlson Oct 8, 2026
0a9bb83
Merge remote-tracking branch 'origin/inventory-descriptions' into HEAD
justcarlson Oct 8, 2026
064767f
Merge pull request #15 from justcarlson/perf/agent-guidance
justcarlson Oct 8, 2026
e99eaf6
perf: reuse secret key derivation across MCP sessions
justcarlson Oct 8, 2026
a53935a
perf(sdk): batch policy and schema reads for a search page
justcarlson Oct 8, 2026
b970ec0
perf: record bounded MCP tool usage and latency
justcarlson Oct 8, 2026
7ed80f5
Merge pull request #17 from justcarlson/perf/mcp-session-initialization
justcarlson Oct 8, 2026
bfdfda6
Merge pull request #19 from justcarlson/perf/tool-usage-metrics
justcarlson Oct 8, 2026
05540fd
Merge pull request #18 from justcarlson/perf/batch-search-schema-policy
justcarlson Oct 8, 2026
b649557
perf: keep detached catalog refresh off request turns
justcarlson Oct 8, 2026
ae2aed7
Merge remote-tracking branch 'origin/inventory-descriptions' into per…
justcarlson Oct 8, 2026
eca0991
perf: serialize detached discoveries and share queued scans
justcarlson Oct 8, 2026
4a17f9e
perf: prioritize cached catalog reads over detached discovery
justcarlson Oct 8, 2026
a60ad9f
fix: isolate unchanged refresh stamps from concurrent rollback
justcarlson Oct 8, 2026
f553028
fix: preserve refresh outage stamps across unrelated rollback
justcarlson Oct 8, 2026
bf82492
Merge pull request #20 from justcarlson/perf/catalog-refresh-isolation
justcarlson Oct 8, 2026
2b1d322
perf: cut per-request MCP auth work and refuse silent standalone streams
justcarlson Oct 8, 2026
fbe0bbe
perf: read the execute description on demand in passthrough sessions
justcarlson Oct 8, 2026
fd7224b
ci: gate inventory-descriptions pull requests
justcarlson Oct 8, 2026
0c7cc2a
fix(auth): require current org membership on every authenticated request
justcarlson Oct 8, 2026
0d1fa0e
Merge pull request #23 from justcarlson/ci/inventory-descriptions-16
justcarlson Oct 8, 2026
ad29798
Merge branch 'inventory-descriptions' into perf/mcp-short-session-setup
justcarlson Oct 8, 2026
d8dd17d
Merge fix/member-revocation into perf/mcp-short-session-setup
justcarlson Oct 8, 2026
99442b6
Merge remote-tracking branch 'origin/inventory-descriptions' into fix…
justcarlson Oct 8, 2026
2d471fe
Merge remote-tracking branch 'origin/perf/mcp-short-session-setup' in…
justcarlson Oct 8, 2026
722606e
Merge pull request #25 from justcarlson/fix/member-revocation
justcarlson Oct 8, 2026
7e498f0
Merge inventory-descriptions after member revocation release gate
justcarlson Oct 8, 2026
9cf5d00
Merge pull request #21 from justcarlson/perf/mcp-short-session-setup
justcarlson Oct 8, 2026
1e3bf22
test: select the invoked passthrough request and restore credential c…
justcarlson Oct 8, 2026
d92c715
Merge remote-tracking branch 'origin/inventory-descriptions' into fix…
justcarlson Oct 8, 2026
a7ec508
feat(execution): resolve integration aliases in sandbox tools.search …
justcarlson Oct 8, 2026
13538dc
docs(mcp): give sandbox sessions server instructions and a lean execu…
justcarlson Oct 8, 2026
d18c9f1
feat(execution): list only integrations with callable tools in the ex…
justcarlson Oct 8, 2026
dc4e833
perf(telemetry): record sandbox execute calls per connected tool
justcarlson Oct 8, 2026
89ff0f1
Merge pull request #30 from justcarlson/fix/22-passthrough-header
justcarlson Oct 8, 2026
2c7e2cf
Merge inventory-descriptions to run current release checks
justcarlson Oct 8, 2026
cb1831f
Merge inventory-descriptions to run current release checks
justcarlson Oct 8, 2026
6297013
Merge inventory-descriptions to run current release checks
justcarlson Oct 8, 2026
9fea378
Merge pull request #31: sandbox tool aliases
justcarlson Oct 8, 2026
450cda9
ci: run release checks for sandbox guidance
justcarlson Oct 8, 2026
5abd4ab
Merge released sandbox aliases and current CI into guidance
justcarlson Oct 8, 2026
bc51202
fix(execution): retain every callable integration namespace
justcarlson Oct 8, 2026
6c9eba0
Merge pull request #32: sandbox server guidance
justcarlson Oct 8, 2026
f982dea
Merge sandbox aliases and guidance into callable inventory
justcarlson Oct 8, 2026
b5d8466
fix(telemetry): retain failed calls and migration sequence
justcarlson Oct 8, 2026
e1b0b7f
Merge sandbox aliases and guidance into execute telemetry
justcarlson Oct 8, 2026
067ca6c
Merge pull request #33 from justcarlson/feat/184-inventory
justcarlson Oct 8, 2026
851107d
Merge inventory-descriptions into sandbox telemetry branch
justcarlson Oct 8, 2026
76a56e1
fix: retain sandbox telemetry through approval pause and resume
justcarlson Oct 8, 2026
26647f3
Merge pull request #34 from justcarlson/feat/184-usage
justcarlson Oct 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name: CI

on:
pull_request:
branches:
- main
push:
branches:
- main
Expand Down
140 changes: 140 additions & 0 deletions .github/workflows/inventory-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
name: Inventory CI

on:
pull_request:
branches:
- inventory-descriptions
push:
branches:
- inventory-descriptions

permissions:
contents: read

concurrency:
group: inventory-ci-${{ github.ref }}
cancel-in-progress: true

env:
DO_NOT_TRACK: "1"
EXECUTOR_DISABLE_ANALYTICS: "1"
EXECUTOR_DISABLE_INTEGRATIONS_FETCH: "1"

jobs:
checks:
name: Inventory ${{ matrix.name }}
runs-on: ubuntu-24.04
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- name: Format
command: bun run format:check
- name: Lint
command: bun run lint
- name: Typecheck
command: bunx turbo run typecheck --filter='./packages/core/*' --filter='./packages/hosts/*' --filter=@executor-js/host-selfhost --concurrency=2
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json
- uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-${{ runner.arch }}-inventory-bun-${{ hashFiles('bun.lock', 'package.json', 'patches/**') }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-inventory-bun-
- run: bun install --frozen-lockfile
- run: bun run check:patches
- run: ${{ matrix.command }}

tests:
name: Inventory ${{ matrix.name }}
runs-on: ubuntu-24.04
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- name: Core and hosts tests
browsers: true
# Forward worker limits, the browser path and telemetry opt-outs.
command: bunx turbo run test --filter='./packages/core/*' --filter='./packages/hosts/*' --concurrency=1 --env-mode=loose
- name: Selfhost tests
browsers: false
command: bun run --cwd apps/host-selfhost test
env:
VITEST_MAX_WORKERS: "2"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json
- uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-${{ runner.arch }}-inventory-bun-${{ hashFiles('bun.lock', 'package.json', 'patches/**') }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-inventory-bun-
- run: bun install --frozen-lockfile
- run: bun run check:patches
- uses: actions/cache@v4
if: matrix.browsers
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-${{ runner.arch }}-inventory-playwright-${{ hashFiles('bun.lock') }}
# The MCP Apps shell suite drives a real Chromium. Use the workspace's
# pinned Playwright and install its Linux libraries on the hosted runner.
- name: Install Chromium
if: matrix.browsers
working-directory: e2e
run: |
bunx playwright install --with-deps chromium
echo "PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=$(bun -e 'import { chromium } from "playwright"; console.log(chromium.executablePath())')" >> "$GITHUB_ENV"
- run: ${{ matrix.command }}

passthrough:
name: Inventory Passthrough e2e
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json
- uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-${{ runner.arch }}-inventory-bun-${{ hashFiles('bun.lock', 'package.json', 'patches/**') }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-inventory-bun-
- run: bun install --frozen-lockfile
- run: bun run check:patches
- uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-${{ runner.arch }}-inventory-playwright-${{ hashFiles('bun.lock') }}
- name: Install Chromium
working-directory: e2e
run: bunx playwright install --with-deps chromium
- name: Run passthrough journey with credential assertion
working-directory: e2e
run: bunx vitest run --project selfhost scenarios/mcp-passthrough.test.ts
- name: Upload passthrough evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: inventory-passthrough-e2e
path: e2e/runs/selfhost/
retention-days: 7
2 changes: 2 additions & 0 deletions .github/workflows/pkg-pr-new.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name: Publish (pkg.pr.new)

on:
pull_request:
branches:
- main

permissions:
contents: read
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ name: Preview

on:
pull_request:
branches:
- main
types: [opened, reopened, synchronize, closed]

permissions:
Expand Down
167 changes: 167 additions & 0 deletions apps/host-selfhost/scripts/bench-mcp-session.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import { mkdtempSync, readFileSync, writeFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { resolve, join } from "node:path";
import { createHash } from "node:crypto";
import { Effect, Layer, Tracer } from "effect";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
import { AuthTemplateSlug, ConnectionName, IntegrationSlug } from "@executor-js/sdk";
import { makeMcpBuildServer, makeScopedExecutor, PluginsProvider } from "@executor-js/api/server";
import { createSelfHostDb, SelfHostDb } from "../src/db/self-host-db";
import { SelfHostExecutionStackLayer, SelfHostScopedExecutorSeams } from "../src/execution";
import type { SelfHostPlugins } from "../src/plugins";
import executorConfig from "../executor.config";

const dataDir = mkdtempSync(join(tmpdir(), "executor-session-bench-"));
process.env.EXECUTOR_DATA_DIR = dataDir;
const db = await createSelfHostDb({ path: join(dataDir, "data.db") });
const dbLayer = Layer.succeed(SelfHostDb)(db);
const samples = Number(process.env.BENCH_SAMPLES ?? 25);
const phases: Record<string, number[]> = {};
const record = (name: string, ms: number) => (phases[name] ??= []).push(ms);
const tracer = Tracer.make({
span(options) {
const span = new Tracer.NativeSpan(options);
const end = span.end.bind(span);
span.end = (time, exit) => {
record(options.name, Number(time - options.startTime) / 1e6);
end(time, exit);
};
return span;
},
});
const pluginsLayer = Layer.succeed(PluginsProvider)({
plugins(context) {
const start = performance.now();
const plugins = executorConfig.plugins({
activeToolkitSlug:
context?.mcpResource?.kind === "toolkit" ? context.mcpResource.slug : undefined,
});
record("plugins.factory", performance.now() - start);
return plugins;
},
});
const stack = SelfHostExecutionStackLayer.pipe(Layer.provide(dbLayer));
const build = makeMcpBuildServer(Layer.merge(stack, pluginsLayer));
const principal = {
accountId: "bench",
organizationId: "bench",
organizationName: "Bench",
email: "bench@example.test",
name: "Bench",
avatarUrl: null,
roles: ["user"],
orgRoleModel: "organization" as const,
};
const digest = (value: unknown) => createHash("sha256").update(JSON.stringify(value)).digest("hex");
const hashes: Record<string, string> = {};
const checkHash = (name: string, value: unknown) => {
const hash = digest(value);
if (hashes[name] !== undefined && hashes[name] !== hash) {
throw new Error(`Benchmark result changed between samples: ${name}`);
}
hashes[name] = hash;
};
const run = <A, E>(effect: Effect.Effect<A, E>) => Effect.runPromise(effect);
try {
const seed = await run(
makeScopedExecutor<SelfHostPlugins>("bench", "bench", "Bench").pipe(
Effect.provide(SelfHostScopedExecutorSeams),
Effect.provide(dbLayer),
),
);
for (const slug of ["cloudflare_api", "vercel_api"]) {
const fixture = slug === "cloudflare_api" ? "cloudflare" : "vercel";
await run(
seed.openapi.addSpec({
spec: {
kind: "blob",
value: readFileSync(
resolve(import.meta.dir, `../../../packages/plugins/openapi/fixtures/${fixture}.json`),
"utf8",
),
},
slug,
}),
);
await run(
seed.connections.create({
owner: "org",
integration: IntegrationSlug.make(slug),
name: ConnectionName.make("main"),
template: AuthTemplateSlug.make("apiKey"),
value: "fixture-token",
}),
);
}
const tools = await run(seed.tools.list({ includeAnnotations: false }));
await run(seed.close());
for (let i = 0; i < samples; i++) {
const start = performance.now();
const built = await run(
build(principal, { mode: "passthrough", artifactsEnabled: false }).pipe(
Effect.withTracer(tracer),
),
);
record("server.build", performance.now() - start);
const [ct, st] = InMemoryTransport.createLinkedPair();
const client = new Client({ name: "fixture-bench", version: "1" }, { capabilities: {} });
await built.mcpServer.connect(st);
try {
let start = performance.now();
await client.connect(ct);
record("client.initialize", performance.now() - start);
start = performance.now();
const listed = await client.listTools();
record("client.tools_list", performance.now() - start);
checkHash("tools", listed);
checkHash("instructions", client.getInstructions());
for (const [name, args] of [
["filtered", { query: "dns record", integration: "cloudflare_api", limit: 3 }],
["full", { query: "dns record", integration: "cloudflare_api", limit: 3, detail: "full" }],
] as const) {
start = performance.now();
const result = await client.callTool({ name: "search", arguments: args });
record(`client.search.${name}`, performance.now() - start);
if (result.isError) throw new Error(JSON.stringify(result));
checkHash(name, result);
}
} finally {
await client.close();
await built.mcpServer.close();
await run(built.engine.shutdown);
if (built.executor) await run(built.executor.close());
}
}
const summary = Object.fromEntries(
Object.entries(phases).map(([name, times]) => {
const sorted = [...times].sort((a, b) => a - b);
return [
name,
{
n: times.length,
p50: sorted[Math.ceil(times.length * 0.5) - 1],
p95: sorted[Math.ceil(times.length * 0.95) - 1],
},
];
}),
);
const result = {
catalogTools: tools.filter((t) => !t.static).length,
samples,
hashes,
summary,
phases,
};
writeFileSync(
process.env.BENCH_OUTPUT ?? join(tmpdir(), "executor-mcp-session-bench.json"),
JSON.stringify(result, null, 2),
);
console.log(JSON.stringify({ catalogTools: result.catalogTools, hashes, summary }, null, 2));
} finally {
await db.close();
rmSync(dataDir, { recursive: true, force: true });
}
// The plugin transport pool has process timers. All benchmark-owned handles
// are closed above; finish the standalone measurement process.
process.exit(0);
Loading