Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 26 additions & 1 deletion apps/cloud/src/mcp/session-durable-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ import {
type McpExecutionOwnerRoute,
} from "@executor-js/cloudflare/mcp/execution-owner-directory";
import { mcpSessionStub } from "@executor-js/cloudflare/mcp/session-stub";
import { buildExecuteDescription, type ResumeResponse } from "@executor-js/execution";
import {
buildExecuteDescription,
parseIntegrationInventory,
type ResumeResponse,
} from "@executor-js/execution";
import { acquireBuildSlot, type BuildSlotHandle } from "./session-build-semaphore";

// The DO meters executions just like the HTTP `/api/*` plane: it builds its
Expand Down Expand Up @@ -182,6 +186,16 @@ const smokeRenderArtifactAfterQuickJsPreload: typeof smokeRenderArtifact = async
// ---------------------------------------------------------------------------

export class McpSessionDOSqlite extends McpAgentSessionDOBase<Env, CloudSessionDbHandle> {
// Set once per `buildMcpServer` call, read back by `sessionFootprintAttributes`
// so the counts land on the base's `McpSessionDO.init` span alongside
// `mcp.isolate.*` residency — see the comment at the `parseIntegrationInventory`
// call site below for why this is free to compute.
private lastSessionFootprint: Record<string, number> = {};

protected override sessionFootprintAttributes(): Record<string, number> {
return this.lastSessionFootprint;
}

protected override sessionTimeoutMs(): number {
return positiveMilliseconds(env.MCP_SESSION_TIMEOUT_MS) ?? super.sessionTimeoutMs();
}
Expand Down Expand Up @@ -327,6 +341,17 @@ export class McpSessionDOSqlite extends McpAgentSessionDOBase<Env, CloudSessionD
const description = yield* buildExecuteDescription(executor).pipe(
Effect.withSpan("mcp.execute.description.build"),
);
// Cheap size proxy for the session's footprint: `parseIntegrationInventory`
// is a regex walk over the description string already built above, the
// same trick `createExecutorMcpServer` uses to derive its per-integration
// search tools without a second `connections.list()` — no new query, no
// catalog serialization. This is a *count* of distinct connected
// integrations, not raw connections (several connections can share one
// integration) and is capped at the description's 50-item inventory
// limit, same as what the model itself sees.
self.lastSessionFootprint = {
"mcp.session.integration_count": parseIntegrationInventory(description).length,
};
const sessionElicitationMode = sessionMeta.elicitationMode ?? "model";
const mcpServer = yield* createExecutorMcpServer({
engine,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,20 @@ export abstract class McpAgentSessionDOBase<
dbHandle: TDbHandle,
): Effect.Effect<BuiltMcpServer>;

/**
* Cheap, count-only proxies for what the runtime `buildMcpServer` just built
* holds — e.g. connected-integration counts — attributed alongside
* `residencyAttributes()` on the same `McpSessionDO.init` span so per-session
* memory footprint is queryable next to isolate residency without a
* cross-span join. Empty by default: a host that has nothing free to read
* (or nothing beyond what `buildMcpServer` already returns) need not
* override this. MUST stay O(1)/O(count) over already-materialized state —
* never trigger a new query or serialize a catalog to compute these.
*/
protected sessionFootprintAttributes(): Record<string, number> {
return {};
}

protected withTelemetry<A, E>(
effect: Effect.Effect<A, E>,
_incoming?: IncomingTraceHeaders,
Expand Down Expand Up @@ -1001,7 +1015,12 @@ export abstract class McpAgentSessionDOBase<
// The gauge on the way up. Paired with the same attributes on
// `mcp.session.idle_runtime_dispose`, this is what shows whether idle
// sessions are actually giving their runtimes back in production.
yield* Effect.annotateCurrentSpan(residencyAttributes());
// `sessionFootprintAttributes()` rides the same span so a heavy session
// can be attributed to what it holds, not just counted.
yield* Effect.annotateCurrentSpan({
...residencyAttributes(),
...self.sessionFootprintAttributes(),
});
// Last statement, and pure bookkeeping: the runtime above is already
// installed and serving. Losing the timestamp/alarm write to a platform
// reset must not undo any of it — the in-memory clock is already set and
Expand Down
Loading