206 lines
7.2 KiB
TypeScript
206 lines
7.2 KiB
TypeScript
/**
|
||
* OmniRoute MCP Session Pool Tools — Manage and monitor anonymous web session pools.
|
||
*
|
||
* Tools:
|
||
* 1. omniroute_pool_status — Get pool stats for one or all providers
|
||
* 2. omniroute_pool_sessions — List per-session details for a provider's pool
|
||
* 3. omniroute_pool_reset — Shut down and recreate a pool
|
||
* 4. omniroute_pool_warm — Warm up a pool to a target session count
|
||
* 5. omniroute_pool_health — Aggregated pool health with breaker state and issues
|
||
*/
|
||
|
||
import { z } from "zod";
|
||
import { PoolRegistry } from "../../services/sessionPool/poolRegistry.ts";
|
||
import { getWebSessionPoolHealth } from "../../services/webSessionPoolHealth.ts";
|
||
import { getBrowserPoolMetrics } from "../../services/browserPool.ts";
|
||
|
||
// ─── Input Schemas ─────────────────────────────────────────────────────────
|
||
|
||
export const poolStatusInput = z.object({
|
||
provider: z
|
||
.string()
|
||
.optional()
|
||
.describe("Provider name (e.g. 'pollinations'). Omit to list all pools"),
|
||
});
|
||
|
||
export const poolSessionsInput = z.object({
|
||
provider: z.string().describe("Provider name (e.g. 'pollinations')"),
|
||
});
|
||
|
||
export const poolResetInput = z.object({
|
||
provider: z.string().describe("Provider name (e.g. 'pollinations')"),
|
||
});
|
||
|
||
export const poolWarmInput = z.object({
|
||
provider: z.string().describe("Provider name (e.g. 'pollinations')"),
|
||
count: z
|
||
.number()
|
||
.int()
|
||
.min(1)
|
||
.max(50)
|
||
.default(6)
|
||
.describe("Target session count (1–50)"),
|
||
});
|
||
|
||
export const poolHealthInput = z.object({
|
||
provider: z
|
||
.string()
|
||
.optional()
|
||
.describe("Provider name (e.g. 'pollinations'). Omit to list all pools"),
|
||
});
|
||
|
||
// ─── Handlers ──────────────────────────────────────────────────────────────
|
||
|
||
/**
|
||
* Handle pool_status tool: return stats for one or all pools
|
||
*/
|
||
export async function handlePoolStatus(
|
||
args: z.infer<typeof poolStatusInput>,
|
||
): Promise<Record<string, unknown>> {
|
||
if (args.provider) {
|
||
const stats = PoolRegistry.getStats(args.provider);
|
||
if (!stats) {
|
||
return { error: `No pool found for provider '${args.provider}'` };
|
||
}
|
||
return { provider: args.provider, stats };
|
||
}
|
||
|
||
const all = PoolRegistry.getAllStats();
|
||
return {
|
||
totalPools: all.length,
|
||
providers: PoolRegistry.listProviders(),
|
||
pools: all,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Handle pool_sessions tool: list per-session details for a provider's pool
|
||
*/
|
||
export async function handlePoolSessions(
|
||
args: z.infer<typeof poolSessionsInput>,
|
||
): Promise<Record<string, unknown>> {
|
||
const details = PoolRegistry.getSessionDetails(args.provider);
|
||
if (!details) {
|
||
return { error: `No pool found for provider '${args.provider}'` };
|
||
}
|
||
|
||
const stats = PoolRegistry.getStats(args.provider);
|
||
return {
|
||
provider: args.provider,
|
||
sessionCount: details.length,
|
||
stats,
|
||
sessions: details,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Handle pool_reset tool: shut down and recreate a pool
|
||
*/
|
||
export async function handlePoolReset(
|
||
args: z.infer<typeof poolResetInput>,
|
||
): Promise<Record<string, unknown>> {
|
||
const existed = PoolRegistry.resetPool(args.provider);
|
||
return {
|
||
provider: args.provider,
|
||
reset: existed,
|
||
message: existed
|
||
? `Pool '${args.provider}' shut down and removed. It will be recreated on next request.`
|
||
: `No pool found for provider '${args.provider}'`,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Handle pool_warm tool: warm up a pool to a target session count
|
||
*/
|
||
export async function handlePoolWarm(
|
||
args: z.infer<typeof poolWarmInput>,
|
||
): Promise<Record<string, unknown>> {
|
||
// If pool doesn't exist yet, we can't warm it
|
||
const pool = PoolRegistry.getPool(args.provider);
|
||
if (!pool) {
|
||
return { error: `No pool found for provider '${args.provider}'` };
|
||
}
|
||
|
||
const before = pool.totalCount;
|
||
await pool.warmUp(args.count);
|
||
const after = pool.totalCount;
|
||
|
||
return {
|
||
provider: args.provider,
|
||
targetCount: args.count,
|
||
sessionsBefore: before,
|
||
sessionsAfter: after,
|
||
created: after - before,
|
||
};
|
||
}
|
||
|
||
export async function handlePoolHealth(
|
||
args: z.infer<typeof poolHealthInput>,
|
||
): Promise<Record<string, unknown>> {
|
||
const report = getWebSessionPoolHealth(args.provider);
|
||
return report as unknown as Record<string, unknown>;
|
||
}
|
||
|
||
export const browserPoolStatusInput = z.object({});
|
||
|
||
/**
|
||
* Handle browser_pool_status tool (#3368 PR7): return the stealth browser
|
||
* pool's live status plus cumulative lifecycle telemetry.
|
||
*/
|
||
export async function handleBrowserPoolStatus(): Promise<Record<string, unknown>> {
|
||
return getBrowserPoolMetrics();
|
||
}
|
||
|
||
// ─── Tool Registry ─────────────────────────────────────────────────────────
|
||
|
||
export const poolTools = {
|
||
omniroute_pool_status: {
|
||
name: "omniroute_pool_status",
|
||
description:
|
||
"Returns session pool status for a specific provider or all providers. Includes session counts by state (active/cooldown/dead), request totals, success rate, and throughput.",
|
||
scopes: ["read:health"],
|
||
inputSchema: poolStatusInput,
|
||
handler: (args: z.infer<typeof poolStatusInput>) => handlePoolStatus(args),
|
||
},
|
||
omniroute_pool_sessions: {
|
||
name: "omniroute_pool_sessions",
|
||
description:
|
||
"Lists all sessions in a provider's pool with per-session details: fingerprint, status, request counts, inflight, cooldown remaining, and age.",
|
||
scopes: ["read:health"],
|
||
inputSchema: poolSessionsInput,
|
||
handler: (args: z.infer<typeof poolSessionsInput>) => handlePoolSessions(args),
|
||
},
|
||
omniroute_pool_reset: {
|
||
name: "omniroute_pool_reset",
|
||
description:
|
||
"Shuts down and removes all sessions for a provider's pool. A new pool will be created automatically on the next request.",
|
||
scopes: ["write:resilience"],
|
||
inputSchema: poolResetInput,
|
||
handler: (args: z.infer<typeof poolResetInput>) => handlePoolReset(args),
|
||
},
|
||
omniroute_pool_warm: {
|
||
name: "omniroute_pool_warm",
|
||
description:
|
||
"Warms a session pool to the specified session count (1–50). Sessions beyond the current count are created with fresh browser fingerprints.",
|
||
scopes: ["write:resilience"],
|
||
inputSchema: poolWarmInput,
|
||
handler: (args: z.infer<typeof poolWarmInput>) => handlePoolWarm(args),
|
||
},
|
||
omniroute_pool_health: {
|
||
name: "omniroute_pool_health",
|
||
description:
|
||
"Returns aggregated web-session pool health: pool stats + circuit breaker state + per-session details + health status (healthy/degraded/down) + issues list.",
|
||
scopes: ["read:health"],
|
||
inputSchema: poolHealthInput,
|
||
handler: (args: z.infer<typeof poolHealthInput>) => handlePoolHealth(args),
|
||
},
|
||
omniroute_browser_pool_status: {
|
||
name: "omniroute_browser_pool_status",
|
||
description:
|
||
"Returns the stealth browser pool's live status (enabled, active contexts, browser running, stealth available, idle age) plus cumulative lifecycle telemetry: browser launches/failures, context create/reuse/evict/release counts, context-create failures, and shutdowns with the last reason.",
|
||
scopes: ["read:health"],
|
||
inputSchema: browserPoolStatusInput,
|
||
handler: () => handleBrowserPoolStatus(),
|
||
},
|
||
};
|