| |
| |
| |
| |
| |
| |
| |
| |
| |
| import { |
| buildRequestEvent, |
| deriveAcceptLanguage, |
| deriveCountry, |
| deriveExecutionRegion, |
| deriveHost, |
| deriveIp, |
| deriveIpCity, |
| deriveIpRegion, |
| deriveReferer, |
| deriveReqBytes, |
| deriveRequestId, |
| deriveSentryTraceId, |
| deriveUserAgent, |
| emitUsageEvents, |
| type RequestReason, |
| type WaitUntilCtx, |
| } from '../../server/_shared/usage'; |
| import type { AuthKind } from '../../server/_shared/usage-identity'; |
| import type { McpAuthContext } from './types'; |
|
|
| |
| |
| |
| export type McpPhase = |
| | 'auth' |
| | 'precheck' |
| | 'billing' |
| | 'limit' |
| | 'dispatch' |
| | 'malformed' |
| | 'transport' |
| | 'ok'; |
|
|
| export interface McpUsage { |
| phase: McpPhase; |
| authKind: AuthKind; |
| customerId: string | null; |
| principalId: string | null; |
| |
| skip: boolean; |
| } |
|
|
| export function createMcpUsage(): McpUsage { |
| return { phase: 'ok', authKind: 'anon', customerId: null, principalId: null, skip: false }; |
| } |
|
|
| |
| |
| |
| export function setUsageContext(usage: McpUsage, context: McpAuthContext): void { |
| if (context.kind === 'pro') { |
| usage.authKind = 'mcp_oauth'; |
| usage.customerId = context.userId; |
| usage.principalId = context.userId; |
| return; |
| } |
| if (context.kind === 'user_key') { |
| usage.authKind = 'user_api_key'; |
| usage.customerId = context.userId; |
| usage.principalId = context.userId; |
| return; |
| } |
| usage.authKind = 'enterprise_api_key'; |
| } |
|
|
| export function mcpReasonFor(phase: McpPhase, status: number): RequestReason { |
| switch (phase) { |
| case 'auth': |
| return status === 503 ? 'auth_unavailable' : 'auth_401'; |
| case 'precheck': |
| return status === 503 ? 'auth_unavailable' : 'tier_403'; |
| case 'billing': |
| |
| |
| |
| |
| return status === 503 ? 'billing_verification_503' : 'tier_403'; |
| case 'limit': |
| return 'rate_limit_429'; |
| case 'dispatch': |
| if (status === 429) return 'rate_limit_429'; |
| if (status === 503) return 'rate_limit_degraded'; |
| return 'ok'; |
| case 'malformed': |
| return 'malformed_request'; |
| case 'transport': |
| return status === 405 ? 'method_not_allowed' : 'malformed_request'; |
| default: |
| return 'ok'; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export function emitMcpRequestEvent( |
| req: Request, |
| res: Response, |
| usage: McpUsage, |
| durationMs: number, |
| ctx?: WaitUntilCtx, |
| ): void { |
| if (!ctx || usage.skip) return; |
| try { |
| const pathname = (() => { |
| try { return new URL(req.url).pathname; } catch { return '/mcp'; } |
| })(); |
| const resBytesRaw = Number(res.headers.get('content-length')); |
| const event = buildRequestEvent({ |
| requestId: deriveRequestId(req), |
| domain: 'mcp', |
| route: pathname, |
| method: req.method, |
| status: res.status, |
| durationMs, |
| reqBytes: deriveReqBytes(req), |
| resBytes: Number.isFinite(resBytesRaw) && resBytesRaw >= 0 ? resBytesRaw : 0, |
| customerId: usage.customerId, |
| principalId: usage.principalId, |
| authKind: usage.authKind, |
| |
| |
| |
| tier: 0, |
| planKey: null, |
| country: deriveCountry(req), |
| ipCity: deriveIpCity(req), |
| ipRegion: deriveIpRegion(req), |
| executionRegion: deriveExecutionRegion(req), |
| executionPlane: 'vercel-edge', |
| originKind: 'mcp', |
| cacheTier: 'no-store', |
| ip: deriveIp(req), |
| userAgent: deriveUserAgent(req), |
| uaHash: null, |
| referer: deriveReferer(req), |
| acceptLanguage: deriveAcceptLanguage(req), |
| host: deriveHost(req), |
| sentryTraceId: deriveSentryTraceId(req), |
| reason: mcpReasonFor(usage.phase, res.status), |
| }); |
| emitUsageEvents(ctx, [event]); |
| } catch { |
| |
| } |
| } |
|
|