( client: Client, serverName: string, setAppState: (f: (prevState: AppState) => AppState) => void, )
| 66 | } |
| 67 | |
| 68 | export function registerElicitationHandler( |
| 69 | client: Client, |
| 70 | serverName: string, |
| 71 | setAppState: (f: (prevState: AppState) => AppState) => void, |
| 72 | ): void { |
| 73 | // Register the elicitation request handler. |
| 74 | // Wrapped in try/catch because setRequestHandler throws if the client wasn't |
| 75 | // created with elicitation capability declared. |
| 76 | try { |
| 77 | client.setRequestHandler(ElicitRequestSchema, async (request, extra) => { |
| 78 | logMCPDebug( |
| 79 | serverName, |
| 80 | `Received elicitation request: ${jsonStringify(request)}`, |
| 81 | ) |
| 82 | |
| 83 | const mode = getElicitationMode(request.params) |
| 84 | |
| 85 | logEvent('tengu_mcp_elicitation_shown', { |
| 86 | mode: mode as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, |
| 87 | }) |
| 88 | |
| 89 | try { |
| 90 | // Run elicitation hooks first - they can provide a response programmatically |
| 91 | const hookResponse = await runElicitationHooks( |
| 92 | serverName, |
| 93 | request.params, |
| 94 | extra.signal, |
| 95 | ) |
| 96 | if (hookResponse) { |
| 97 | logMCPDebug( |
| 98 | serverName, |
| 99 | `Elicitation resolved by hook: ${jsonStringify(hookResponse)}`, |
| 100 | ) |
| 101 | logEvent('tengu_mcp_elicitation_response', { |
| 102 | mode: mode as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, |
| 103 | action: |
| 104 | hookResponse.action as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, |
| 105 | }) |
| 106 | return hookResponse |
| 107 | } |
| 108 | |
| 109 | const elicitationId = |
| 110 | mode === 'url' && 'elicitationId' in request.params |
| 111 | ? (request.params.elicitationId as string | undefined) |
| 112 | : undefined |
| 113 | |
| 114 | const response = new Promise<ElicitResult>(resolve => { |
| 115 | const onAbort = () => { |
| 116 | resolve({ action: 'cancel' }) |
| 117 | } |
| 118 | |
| 119 | if (extra.signal.aborted) { |
| 120 | onAbort() |
| 121 | return |
| 122 | } |
| 123 | |
| 124 | const waitingState: ElicitationWaitingState | undefined = |
| 125 | elicitationId ? { actionLabel: 'Skip confirmation' } : undefined |
no test coverage detected