MCPcopy Create free account
hub / github.com/adcontextprotocol/adcp / processMessage

Method processMessage

server/src/addie/claude-client.ts:497–1087  ·  view source on GitHub ↗

* Process a message and return a response * Uses database-backed rules for the system prompt when available * * @param userMessage - The user's message * @param threadContext - Optional thread history * @param requestTools - Optional per-request tools (e.g., user-scoped member tools)

(
    userMessage: string,
    threadContext?: Array<{ user: string; text: string }>,
    requestTools?: RequestTools,
    rulesOverride?: RulesOverride,
    options?: ProcessMessageOptions
  )

Source from the content-addressed store, hash-verified

495 * @param options - Optional processing options (e.g., maxIterations for admin users)
496 */
497 async processMessage(
498 userMessage: string,
499 threadContext?: Array<{ user: string; text: string }>,
500 requestTools?: RequestTools,
501 rulesOverride?: RulesOverride,
502 options?: ProcessMessageOptions
503 ): Promise<AddieResponse> {
504 // #2950: warn when a caller has neither `costScope` nor explicit
505 // `uncapped: true`. Silent default meant a future user-facing
506 // caller could ship uncapped and nobody would notice — this log
507 // turns that into an observability signal. A hard throw would
508 // break legitimate callers we haven't migrated yet; loud-log
509 // lets audit rules alert on the event.
510 if (!options?.costScope && !options?.uncapped) {
511 logger.warn(
512 { event: 'cost_cap_unwired', method: 'processMessage' },
513 'claude-client called without costScope or uncapped:true — cost cap silently bypassed',
514 );
515 }
516
517 // #2790: per-user Anthropic cost cap. Check at entry; when the
518 // user has exhausted their daily budget, return a friendly
519 // "try again later" response instead of firing another
520 // (billable) Claude call. The caller's ProcessMessageOptions
521 // carries both `userId` and `tier` so we don't have to resolve
522 // the subscription tier here.
523 if (options?.costScope) {
524 const capResult = await checkCostCap(
525 options.costScope.userId,
526 options.costScope.tier,
527 );
528 if (!capResult.ok) {
529 const message = formatCapExceededMessage(capResult);
530 logger.warn(
531 {
532 userId: options.costScope.userId,
533 tier: options.costScope.tier,
534 spentCents: capResult.spentCents,
535 retryAfterMs: capResult.retryAfterMs,
536 },
537 'Addie cost cap exceeded — refusing Claude call',
538 );
539 return {
540 text: message,
541 tools_used: [],
542 tool_executions: [],
543 flagged: true,
544 flag_reason: 'cost_cap_exceeded',
545 };
546 }
547 }
548
549 const toolsUsed: string[] = [];
550 const toolExecutions: ToolExecution[] = [];
551 let executionSequence = 0;
552
553 // Timing metrics
554 const timingStart = Date.now();

Callers 12

createAddieChatRouterFunction · 0.80
handleChatToolFunction · 0.80
handleUserMessageFunction · 0.80
handleAppMentionFunction · 0.80
handleDirectMessageFunction · 0.80
handleActiveThreadReplyFunction · 0.80
handleChannelMessageFunction · 0.80
handleReactionAddedFunction · 0.80
handleEmailConversationFunction · 0.80
handleAssistantMessageFunction · 0.80
handleAppMentionFunction · 0.80

Calls 15

getSystemPromptMethod · 0.95
logPromptOverflowMethod · 0.95
summarizeToolResultMethod · 0.95
checkCostCapFunction · 0.85
formatCapExceededMessageFunction · 0.85
formatTokenCountFunction · 0.85
toAnthropicMessagesFunction · 0.85

Tested by

no test coverage detected