Skip to main content
Glama
confirm_output.ts4.79 kB
import type { ToolHandler, ToolContext, ToolResponse, ToolMetadata, SessionConfig } from './types.js'; import { createSuccessResponse, createErrorResponse } from './types.js'; // One-time token store for deferred payload generation (closures) type PayloadThunk = () => Promise<string> | string; const STORE = new Map<string, { thunk: PayloadThunk; expiresAt: number }>(); function now() { return Date.now(); } function purgeExpired(): void { const t = now(); for (const [k, v] of STORE.entries()) { if (v.expiresAt <= t) STORE.delete(k); } } function genToken(len = 12): string { return Math.random().toString(36).slice(2, 2 + len); } export function registerPayloadThunk(thunk: PayloadThunk, ttlMs = 120000): string { purgeExpired(); const token = genToken(); const expiresAt = now() + Math.max(1000, ttlMs); STORE.set(token, { thunk, expiresAt }); return token; } export function consumeThunk(token: string): { ok: true; thunk: PayloadThunk } | { ok: false; error: string } { purgeExpired(); const entry = STORE.get(token); if (!entry) return { ok: false, error: 'Invalid or expired token' }; STORE.delete(token); if (entry.expiresAt <= now()) return { ok: false, error: 'Invalid or expired token' }; return { ok: true, thunk: entry.thunk }; } // Preview helpers (moved here from confirmHelpers.ts for easier discovery) export interface PreviewCounts { totalLength?: number; shownLength?: number; totalMatched?: number; shownCount?: number; truncated?: boolean; } export function makeConfirmPreview( payloadOrThunk: string | PayloadThunk, options: { headerLine?: string; previewLines: string[]; // include your own 'Preview ...' label line(s) counts?: PreviewCounts; extraTips?: string[]; } ): { token: string; lines: string[] } { const thunk: PayloadThunk = typeof payloadOrThunk === 'function' ? (payloadOrThunk as PayloadThunk) : () => (payloadOrThunk as string); const token = registerPayloadThunk(thunk); const lines: string[] = []; if (options.headerLine) { lines.push(options.headerLine); } const parts: string[] = []; const c = options.counts || {}; if (typeof c.totalLength === 'number') parts.push(`totalLength=${c.totalLength}`); if (typeof c.shownLength === 'number') parts.push(`shownLength=${c.shownLength}`); if (typeof c.totalMatched === 'number') parts.push(`totalMatched=${c.totalMatched}`); if (typeof c.shownCount === 'number') parts.push(`shownCount=${c.shownCount}`); if (typeof c.truncated === 'boolean') parts.push(`truncated=${c.truncated}`); if (parts.length) { lines.push(`counts: ${parts.join(', ')}`); } if (lines.length) lines.push(''); // Caller provides preview content lines (e.g., label + excerpt or list) for (const l of options.previewLines) lines.push(l); if (options.previewLines.length) lines.push(''); const baseLen = typeof c.totalLength === 'number' ? c.totalLength : (typeof payloadOrThunk === 'string' ? payloadOrThunk.length : 0); const estTokens = Math.round(baseLen / 3); lines.push( `Output is large (~${estTokens} tokens). To fetch full content without resending parameters, call confirm_output({ token: "${token}" }).` ); if (options.extraTips && options.extraTips.length) { for (const tip of options.extraTips) lines.push(tip); } return { token, lines }; } export class ConfirmOutputTool implements ToolHandler { constructor(private server: any) {} static getMetadata(sessionConfig?: SessionConfig): ToolMetadata { return { name: 'confirm_output', description: "Return full output for a previously previewed large result using a one-time token. Use when a tool responded with a preview + token. Safer than resending original parameters.", outputs: [ "Full original payload if token is valid (one-time)", "Error: 'Invalid or expired token'", ], inputSchema: { type: 'object', properties: { token: { type: 'string', description: "One-time token obtained from a tool's preview response", }, }, required: ['token'], }, priority: 2, category: 'Other', }; } async execute(args: any, context: ToolContext): Promise<ToolResponse> { const token = typeof args.token === 'string' ? args.token : ''; if (!token) return createErrorResponse('Token is required'); const res = consumeThunk(token); if (!res.ok) { const err = (res as { ok: false; error: string }).error; return createErrorResponse(err); } try { const out = await res.thunk(); return createSuccessResponse(out); } catch (e) { return createErrorResponse((e as Error).message || 'Failed to produce output'); } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/antonzherdev/mcp-web-inspector'

If you have feedback or need assistance with the MCP directory API, please join our Discord server