Repository navigation
Fix KG orphan rows, log volume, unverified-email MCP refusal, expired token status - #1186
Merged
Merged
Conversation
kg_value_seen.connector_id had no foreign key, so deleted connectors left their value hashes behind and every correlate pass tried to create kg_nodes for them (21.8k FK errors a day on cloud). The migration deletes the orphans and adds the foreign key (ON DELETE CASCADE, NOT VALID + VALIDATE) with an index on connector_id. The ingest now only pairs rows of connectors that existed at the start of the run, caches a refused node per pass, and drops value rows of a connector deleted mid-run instead of failing the batch.
Request logs carried every request and response header (about 1.2 KB per /mcp request) and the MCP SDK printed its responseMode 'json' warning for every per-request handler (61k lines a day), so the backend docker log only reached back about 5.7 hours. Requests now log id, method, path without query, MCP method, user agent and client address; responses only the status. The JSON-mode warning is logged once per process; other console warnings are untouched.
The guard refused /mcp for unverified cloud users with a plain 403, which MCP clients show as a broken connection: 13 users who had connected Claude were refused 438 times in under six hours with no explanation. The refusal now goes through a filter that answers with a minimal MCP server: the notice (masked address, sign-in link for a new code) as instructions and as the only tool, and as the result of any tool call. Nothing of the workspace is read and no email is sent, so the verification email rate is unchanged.
A token marked expiring whose token_expires_at had passed stayed a warning when the connector was not called again, and its owner was never told (a meta-ads token expired on 20 Oct at 17:00 was still expiring a day later). An hourly sweep now marks such connectors needs_reauth (token_expired) against the same credential, which sends the single owner email, and the bearer renewal service does the same when it sees an expired token.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four fixes from the 21 Oct cloud review, one commit each.
Changes
kg_value_seen.connector_idhad no foreign key, so deleted connectors left their value hashes behind (102 connectors, 57.5k rows on cloud), andcorrelate()kept trying to createkg_nodesfor them: 21.8k FK errors a day in the Postgres log.20261021100000_kg_value_seen_connector_fkdeletes the orphans and addskg_value_seen_connector_id_fkey(ON DELETE CASCADE) plus an index onconnector_id, so the cascade doesn't scan the table every time a connector is deleted.kg_*tables already had their FKs (nodes and state cascade, skills set null, edges via nodes).connector_id3.8 s, so the index build should take about as long. Onlykg_value_seenwrites are blocked for those few seconds;connectorswrites are blocked for about a second.correlate()only pairs rows of connectors that existed at the start of the run.nodeId()caches a refused (P2003) node for the rest of the pass instead of retrying it once per value. The value insert drops rows of a connector deleted mid-run instead of failing the whole batch.responseMode: 'json'warning on every per-request handler (61k lines a day).id,method,url(no query string),mcpMethod,userAgent,ipandstatusCode(pino still addsresponseTime). Only named headers are read, so no auth header can reach a line; the existing redaction stays as a backup.createQuietMcpHandlerlogs the JSON-mode warning once per process. It intercepts only that one message, and only while the handler is being built (synchronously). Other warnings pass through.docs/operations/observability.mdis updated to match.McpEmailVerificationRequiredException, and a global filter answers it with a minimal MCP server:anythingmcp_verify_email;tools/callreturnsisErrorwith the notice.ja***@example.com) and says to sign in at<FRONTEND_URL>/loginto get a new code.subscriptions/listenstill get a 403, now with the notice as a JSON-RPC error. The request is still refused before any controller runs, nothing of the workspace is read, and no email is sent from this path, so the verification-email rate is unchanged./mcp, the refusal is unchanged.expiring. The connector's last call was at 16:45 on 20 Oct, before the 17:00 expiry. With no later call, nothing ever re-checked the token.ConnectorAuthStatusServicenow runs an hourly sweep (first run 2 min after boot, timers unref'd). It findsauth_status='expiring' AND token_expires_at <= now(), marks each connectorneeds_reauth/token_expiredagainst the same credential ref, and the existing claim sends the single owner email.BearerTokenRenewalServicedoes the same when a call sees an already-expired token. The call still goes out, so Meta's answer has the last word.expiring/token_expires_at, so OAuth2 connectors are not touched.Deployment modes
Type
Testing
prisma migrate diffagainst the schema is empty.Commands, in
packages/backend:npx tsc --noEmit -p tsconfig.json: clean.npm run lint: 0 errors, 11 warnings, none in changed files. Lint's unrelated rewrites of two spec files were reverted.npm test: 729 suites passed, 4 skipped; 21,845 tests passed, 1,538 skipped.Checklist
npm test)