feat(mcp): support vw_beta_promo_2026 public token for external agents
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 6s

This commit is contained in:
OG T
2026-06-07 21:15:56 +08:00
parent ce53b82d9d
commit bc325fd650
9 changed files with 139 additions and 6 deletions

View File

@@ -276,7 +276,12 @@ export async function POST(request: NextRequest, props: { params: Promise<{ tool
}
const token = authHeader.split(" ")[1];
if (process.env.API_KEY && token !== process.env.API_KEY) {
// 試營運推廣期間,允許外部 Agent 使用這組公版 Token 來接案
const isBetaToken = token === "vw_beta_promo_2026";
const isValidServerKey = process.env.API_KEY && token === process.env.API_KEY;
if (!isValidServerKey && !isBetaToken) {
void sendTrafficAlert({
level: "warning",
action: "EXTERNAL_MCP_AUTH_FORBIDDEN",

View File

@@ -5,6 +5,7 @@ import Stripe from "stripe";
const stripe = process.env.STRIPE_SECRET_KEY ? new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: "2026-05-27.dahlia", // Use latest or your specific API version
}) : null;
const ALLOW_MCP_CLAIM_WITHOUT_STRIPE = process.env.ALLOW_MCP_CLAIM_WITHOUT_STRIPE === "true";
export async function authHold(
tx: Prisma.TransactionClient,
@@ -26,6 +27,19 @@ export async function authHold(
// Check if we already have a payment intent for this task
const task = await tx.task.findUnique({ where: { id: taskId } });
if (!task || !task.stripe_payment_intent_id) {
if (ALLOW_MCP_CLAIM_WITHOUT_STRIPE) {
return await tx.ledgerEntry.create({
data: {
task_id: taskId,
phase: "AUTH_HOLD",
idempotency_key: idempotencyKey,
stripe_object_id: null,
response_status: "SKIPPED_NO_STRIPE_INTENT",
http_status: 200,
},
});
}
throw new Error("Task does not have a stripe_payment_intent_id. It must be created via Scout checkout first.");
}

View File

@@ -10,6 +10,8 @@ type FunnelSummary = {
judgeFailEvents: number;
openTaskCount: number;
sampleOpenTasks: string[];
mcpAuthMissingEvents: number;
mcpAuthForbiddenEvents: number;
externalOpenedActors: number;
externalClaimingActors: number;
externalSubmittingActors: number;
@@ -172,6 +174,8 @@ async function fetchFunnelSummary(minutes: number): Promise<FunnelSummary> {
(actionSummary["EXTERNAL_LIST_OPEN_TASKS_MCP"] || 0);
const claimEvents = actionSummary["EXTERNAL_CLAIM_TASK_SUCCESS"] || 0;
const submitEvents = actionSummary["EXTERNAL_SUBMIT_SOLUTION_SUCCESS"] || 0;
const mcpAuthMissingEvents = actionSummary["EXTERNAL_MCP_AUTH_MISSING"] || 0;
const mcpAuthForbiddenEvents = actionSummary["EXTERNAL_MCP_AUTH_FORBIDDEN"] || 0;
const judgePassEvents = judgeRows.filter((row) => {
const metadata = asRecordJson(row.metadata);
@@ -232,6 +236,8 @@ async function fetchFunnelSummary(minutes: number): Promise<FunnelSummary> {
judgeFailEvents,
openTaskCount,
sampleOpenTasks,
mcpAuthMissingEvents,
mcpAuthForbiddenEvents,
externalOpenedActors,
externalClaimingActors,
externalSubmittingActors,
@@ -250,6 +256,8 @@ function buildAlertMessage(rule: string, summary: FunnelSummary) {
claimEvents,
submitEvents,
judgePassEvents,
mcpAuthMissingEvents,
mcpAuthForbiddenEvents,
payoutCaptured,
openTaskCount,
sampleOpenTasks,
@@ -260,10 +268,24 @@ function buildAlertMessage(rule: string, summary: FunnelSummary) {
topOpenOnlyActors,
} = summary;
const authBarrierEvents = mcpAuthMissingEvents + mcpAuthForbiddenEvents;
const topActorSummary = topOpenOnlyActors
.slice(0, 3)
.map((actor) => `${actor.actorId}(${actor.opens})`)
.join(", ");
const authBarrierHint =
authBarrierEvents > 0
? `偵測到 MCP 權限攔截: AUTH missing=${mcpAuthMissingEvents}FORBIDDEN=${mcpAuthForbiddenEvents}` +
`請先確認外部 agent 是否已帶 ` +
"`Authorization: Bearer <YOUR_API_KEY>`。"
: "";
switch (rule) {
case "EXTERNAL_FUNNEL_CLAIM_STALL":
return `外部曝光已達 ${discoveryEvents}(最近 ${periodMinutes} 分鐘),待接任務 ${openTaskCount}但尚無接案EXTERNAL_CLAIM_TASK_SUCCESS = ${claimEvents})。` +
`${sampleOpenTasks.length > 0 ? `可用任務樣本: ${sampleOpenTasks.join(", ")}` : ""}` +
`${topActorSummary ? `高活躍 Actor尚未接案: ${topActorSummary}` : ""}` +
`${authBarrierHint}` +
`請檢查任務是否包含可直接執行的 npx 指令與明確交付條件。`;
case "EXTERNAL_FUNNEL_SUBMIT_STALL":
return `外部已有 ${claimEvents} 筆接案,但近期 ${periodMinutes} 分鐘無任何提交EXTERNAL_SUBMIT_SOLUTION_SUCCESS = ${submitEvents})。請先加速回傳格式與驗收測試規格。`;
@@ -276,7 +298,8 @@ function buildAlertMessage(rule: string, summary: FunnelSummary) {
`外部 Actor= ${externalOpenedActors} 位,` +
`已接案=${externalClaimingActors}、已提交=${externalSubmittingActors}` +
`仍停在曝光僅曝光階段 ${externalOnlyOpenActors} 位。` +
`${topOpenOnlyActors.length ? `先看未進一步的熱門 Actor${topOpenOnlyActors.map((actor) => `${actor.actorId}(${actor.opens})`).join(", ")}` : ""}`;
`${topActorSummary ? `先看未進一步的熱門 Actor${topActorSummary}` : ""}` +
`${authBarrierHint}`;
default:
return "外部 AI 流量轉化斷崖異常。";
}
@@ -374,6 +397,8 @@ export async function evaluateExternalFunnelHealth(input: MonitorInput): Promise
external_claiming_actors: summary.externalClaimingActors,
external_submitting_actors: summary.externalSubmittingActors,
external_only_open_actors: summary.externalOnlyOpenActors,
mcp_auth_missing_events: summary.mcpAuthMissingEvents,
mcp_auth_forbidden_events: summary.mcpAuthForbiddenEvents,
payout_captured: summary.payoutCaptured,
payout_released: summary.payoutReleased,
period_minutes: summary.periodMinutes,