fix(revenuecat): 🐛 pick result frame from MCP SSE stream; cover platform sync/keys in scope-403
This commit is contained in:
@@ -39,19 +39,33 @@ export const callRcMcpTool = async ({
|
||||
throw new Error(`RevenueCat MCP error (${response.status})`);
|
||||
}
|
||||
|
||||
// Streamable-HTTP MCP replies as SSE: pull the JSON out of the `data:` line.
|
||||
// Streamable-HTTP MCP replies as SSE and may emit preamble frames (ping,
|
||||
// progress) before the result — pick the frame that carries result/error.
|
||||
const text = await response.text();
|
||||
const dataLine = text
|
||||
const candidates = text
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.find((line) => line.startsWith("data:") || line.startsWith("{"));
|
||||
const payload = dataLine?.replace(/^data:\s*/, "") ?? text.trim();
|
||||
.filter((line) => line.startsWith("data:") || line.startsWith("{"))
|
||||
.map((line) => line.replace(/^data:\s*/, ""));
|
||||
|
||||
let parsed: JsonRpcResult;
|
||||
try {
|
||||
parsed = JSON.parse(payload) as JsonRpcResult;
|
||||
} catch {
|
||||
throw new Error(`RevenueCat MCP returned unparseable response: ${payload.slice(0, 200)}`);
|
||||
let parsed: JsonRpcResult | undefined;
|
||||
for (const candidate of candidates) {
|
||||
let frame: JsonRpcResult;
|
||||
try {
|
||||
frame = JSON.parse(candidate) as JsonRpcResult;
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
if (frame.result !== undefined || frame.error !== undefined) {
|
||||
parsed = frame;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!parsed) {
|
||||
throw new Error(
|
||||
`RevenueCat MCP returned no result frame: ${text.slice(0, 200)}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (parsed.error) {
|
||||
|
||||
@@ -1370,6 +1370,36 @@ const ROUTES = [
|
||||
needsScopes: true,
|
||||
isWebhookExempt: false,
|
||||
},
|
||||
{
|
||||
handlerName: "handleSyncRevenueCat",
|
||||
handlerFile:
|
||||
"src/internal/platform/platformBeta/handlers/handleSyncRevenueCat.ts",
|
||||
method: "POST",
|
||||
path: "/v1/platform.sync_revenuecat",
|
||||
style: "RPC",
|
||||
group: "v1/platform",
|
||||
mountChain: ["/v1", "", "", "/platform.sync_revenuecat"],
|
||||
sourceRouterFile:
|
||||
"src/internal/platform/platformBeta/platformRpcRouter.ts",
|
||||
routeKind: "createRoute",
|
||||
needsScopes: true,
|
||||
isWebhookExempt: false,
|
||||
},
|
||||
{
|
||||
handlerName: "handleGetRevenueCatKeys",
|
||||
handlerFile:
|
||||
"src/internal/platform/platformBeta/handlers/handleGetRevenueCatKeys.ts",
|
||||
method: "POST",
|
||||
path: "/v1/platform.get_revenuecat_keys",
|
||||
style: "RPC",
|
||||
group: "v1/platform",
|
||||
mountChain: ["/v1", "", "", "/platform.get_revenuecat_keys"],
|
||||
sourceRouterFile:
|
||||
"src/internal/platform/platformBeta/platformRpcRouter.ts",
|
||||
routeKind: "createRoute",
|
||||
needsScopes: true,
|
||||
isWebhookExempt: false,
|
||||
},
|
||||
{
|
||||
handlerName: "handleCreateSchedule",
|
||||
handlerFile: "src/internal/billing/v2/handlers/handleCreateSchedule.ts",
|
||||
@@ -4269,6 +4299,20 @@ const SCOPE_DECISIONS: Record<
|
||||
note: "platform RPC route — write",
|
||||
decidedAt: "2026-06-01T00:00:00.000Z",
|
||||
},
|
||||
"POST|/v1/platform.sync_revenuecat|handleSyncRevenueCat": {
|
||||
decision: "decided",
|
||||
scopes: ["platform:write"],
|
||||
shape: "array",
|
||||
note: "platform RPC route — write",
|
||||
decidedAt: "2026-06-01T00:00:00.000Z",
|
||||
},
|
||||
"POST|/v1/platform.get_revenuecat_keys|handleGetRevenueCatKeys": {
|
||||
decision: "decided",
|
||||
scopes: ["platform:write"],
|
||||
shape: "array",
|
||||
note: "platform RPC route — write",
|
||||
decidedAt: "2026-06-01T00:00:00.000Z",
|
||||
},
|
||||
"POST|/v1/billing.create_schedule|handleCreateSchedule": {
|
||||
decision: "decided",
|
||||
scopes: ["billing:write"],
|
||||
|
||||
Reference in New Issue
Block a user