mcp-scraper 0.38.2 → 0.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/package.json +5 -6
- package/dist/bin/api-server.cjs +0 -58752
- package/dist/bin/api-server.cjs.map +0 -1
- package/dist/bin/api-server.d.cts +0 -1
- package/dist/bin/api-server.d.ts +0 -1
- package/dist/bin/api-server.js +0 -38
- package/dist/bin/api-server.js.map +0 -1
- package/dist/bin/mcp-scraper-cli.cjs +0 -2671
- package/dist/bin/mcp-scraper-cli.cjs.map +0 -1
- package/dist/bin/mcp-scraper-cli.d.cts +0 -1
- package/dist/bin/mcp-scraper-cli.d.ts +0 -1
- package/dist/bin/mcp-scraper-cli.js +0 -742
- package/dist/bin/mcp-scraper-cli.js.map +0 -1
- package/dist/bin/mcp-scraper-install.cjs +0 -129
- package/dist/bin/mcp-scraper-install.cjs.map +0 -1
- package/dist/bin/mcp-scraper-install.d.cts +0 -1
- package/dist/bin/mcp-scraper-install.d.ts +0 -1
- package/dist/bin/mcp-scraper-install.js +0 -27
- package/dist/bin/mcp-scraper-install.js.map +0 -1
- package/dist/bin/mcp-stdio-server.cjs +0 -12264
- package/dist/bin/mcp-stdio-server.cjs.map +0 -1
- package/dist/bin/mcp-stdio-server.d.cts +0 -1
- package/dist/bin/mcp-stdio-server.d.ts +0 -1
- package/dist/bin/mcp-stdio-server.js +0 -135
- package/dist/bin/mcp-stdio-server.js.map +0 -1
- package/dist/bin/paa-harvest.cjs +0 -3808
- package/dist/bin/paa-harvest.cjs.map +0 -1
- package/dist/bin/paa-harvest.d.cts +0 -1
- package/dist/bin/paa-harvest.d.ts +0 -1
- package/dist/bin/paa-harvest.js +0 -44
- package/dist/bin/paa-harvest.js.map +0 -1
- package/dist/chunk-345BQXZH.js +0 -712
- package/dist/chunk-345BQXZH.js.map +0 -1
- package/dist/chunk-44HZLHDV.js +0 -52
- package/dist/chunk-44HZLHDV.js.map +0 -1
- package/dist/chunk-AZRPG43B.js +0 -617
- package/dist/chunk-AZRPG43B.js.map +0 -1
- package/dist/chunk-CB5C3BPB.js +0 -135
- package/dist/chunk-CB5C3BPB.js.map +0 -1
- package/dist/chunk-EGJKUB4Q.js +0 -276
- package/dist/chunk-EGJKUB4Q.js.map +0 -1
- package/dist/chunk-FQI5PFE7.js +0 -1866
- package/dist/chunk-FQI5PFE7.js.map +0 -1
- package/dist/chunk-FRYT3ID4.js +0 -684
- package/dist/chunk-FRYT3ID4.js.map +0 -1
- package/dist/chunk-G3P3ZDB4.js +0 -69
- package/dist/chunk-G3P3ZDB4.js.map +0 -1
- package/dist/chunk-K443GQY5.js +0 -24
- package/dist/chunk-K443GQY5.js.map +0 -1
- package/dist/chunk-N7KUTTCC.js +0 -3007
- package/dist/chunk-N7KUTTCC.js.map +0 -1
- package/dist/chunk-NGM237OO.js +0 -3410
- package/dist/chunk-NGM237OO.js.map +0 -1
- package/dist/chunk-NKCCGADE.js +0 -11285
- package/dist/chunk-NKCCGADE.js.map +0 -1
- package/dist/chunk-NNW3O6ZD.js +0 -108
- package/dist/chunk-NNW3O6ZD.js.map +0 -1
- package/dist/chunk-QZXKQB7Y.js +0 -414
- package/dist/chunk-QZXKQB7Y.js.map +0 -1
- package/dist/chunk-SFRMFGQ6.js +0 -158
- package/dist/chunk-SFRMFGQ6.js.map +0 -1
- package/dist/chunk-YCI2PNCS.js +0 -499
- package/dist/chunk-YCI2PNCS.js.map +0 -1
- package/dist/chunk-YODBNTTN.js +0 -7
- package/dist/chunk-YODBNTTN.js.map +0 -1
- package/dist/db-C5KVCOYT.js +0 -239
- package/dist/db-C5KVCOYT.js.map +0 -1
- package/dist/extract-bundle-KUBX6N6Z.js +0 -568
- package/dist/extract-bundle-KUBX6N6Z.js.map +0 -1
- package/dist/index.cjs +0 -4160
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -413
- package/dist/index.d.ts +0 -413
- package/dist/index.js +0 -338
- package/dist/index.js.map +0 -1
- package/dist/location-data-repository-TTWF3OTM.js +0 -35
- package/dist/location-data-repository-TTWF3OTM.js.map +0 -1
- package/dist/server-5EX6XBIA.js +0 -33596
- package/dist/server-5EX6XBIA.js.map +0 -1
- package/dist/site-extract-repository-XSPJTCIL.js +0 -62
- package/dist/site-extract-repository-XSPJTCIL.js.map +0 -1
- package/dist/worker-XCPU4YSN.js +0 -142
- package/dist/worker-XCPU4YSN.js.map +0 -1
- package/docs/adr/0001-in-page-graphql-interception-for-anti-bot-scraping.md +0 -58
- package/docs/adr/0002-hybrid-smart-rag-vault-retrieval.md +0 -62
- package/docs/adr/0003-waive-unrecoverable-scheduled-model-cost.md +0 -22
- package/docs/adr/README.md +0 -13
- package/docs/final-tooling-spec.md +0 -206
- package/docs/hosted-location-data.md +0 -108
- package/docs/kernel-proxy-future-enhancements.md +0 -80
- package/docs/mcp-tool-craft-lint.generated.md +0 -183
- package/docs/mcp-tool-design-guide.md +0 -225
- package/docs/mcp-tool-manifest.generated.json +0 -22871
- package/docs/mcp-tool-quality-spec.md +0 -240
- package/docs/oauth-legal-review.md +0 -38
- package/docs/seo-crawl-report-spec.md +0 -287
- package/docs/specs/api-forge-spec.md +0 -234
- package/docs/specs/connected-services-control-plane-decoupling-spec.md +0 -1044
- package/docs/specs/deferred-work-spec.md +0 -86
- package/docs/specs/google-drive-bulk-access-and-mcp-schema-passthrough-spec.md +0 -1689
- package/docs/specs/kernel-stealth-captcha-test-matrix.md +0 -278
- package/docs/specs/main-mcp-integration-ownership-spec.md +0 -1164
- package/docs/specs/mcp-tool-definition-quality-audit-spec.md +0 -1602
- package/docs/specs/meta-ad-creative-media-resolution-spec.md +0 -31
- package/docs/specs/multimodal-image-memory-architecture-spec.md +0 -1022
- package/docs/specs/oauth-mcp-spec.md +0 -213
- package/docs/specs/query-fanout-transport-contract-fix.md +0 -45
- package/docs/specs/relationship-workspace-ai-behavior-plan.md +0 -26
- package/docs/specs/unified-credit-and-scheduled-execution-billing-spec.md +0 -995
- package/docs/tool-catalog-spec.md +0 -388
|
@@ -1,1044 +0,0 @@
|
|
|
1
|
-
# Connected Services Control Plane Decoupling
|
|
2
|
-
|
|
3
|
-
- Status: Superseded on 2026-07-16 by `main-mcp-integration-ownership-spec.md`
|
|
4
|
-
- Spec date: 2026-07-16
|
|
5
|
-
- Incident driver: Search Console connection displayed `Connected` while every live read and export returned `connection_transport_unavailable`
|
|
6
|
-
- Primary repos: `mcp-scraper`, `mcp-scraper-scheduler`
|
|
7
|
-
- Adjacent release surfaces: Vercel production projects, root MCP tool contract, generated frontend bundle, billing ledger, Nango Cloud
|
|
8
|
-
- Supersedes: using the scheduler deployment as the unnamed runtime for direct connected-service operations
|
|
9
|
-
- Preserves: the connected-account and usage-pricing decisions in `unified-credit-and-scheduled-execution-billing-spec.md`
|
|
10
|
-
|
|
11
|
-
> Supersession note: this document correctly identified the hidden scheduler dependency and the need to separate lifecycle from operational health, but it placed integration execution in a separate control-plane deployment. Product ownership requires integrations to live on the main MCP. The replacement specification moves connection storage, credentials, provider execution, health, lifecycle controls, and connected-usage settlement into `mcp-scraper`; the Mastra scheduler remains an independent consumer.
|
|
12
|
-
|
|
13
|
-
## Executive decision
|
|
14
|
-
|
|
15
|
-
MCP Scraper will separate **connected-service execution** from **scheduled execution**.
|
|
16
|
-
|
|
17
|
-
The system needs a secure service that owns tenant connection routing, Nango credentials, live tool discovery, provider calls, connection lifecycle operations, usage metering, and operational health. That service is not conceptually a scheduler and must not share the scheduler's deployment availability boundary.
|
|
18
|
-
|
|
19
|
-
Create a dedicated **Connected Services Control Plane** deployment. The first implementation is extracted from `mcp-scraper-scheduler` into a reusable integration module and deployed as an independent Vercel project. This avoids moving credentials into the public API and avoids migrating the existing Postgres connection records.
|
|
20
|
-
|
|
21
|
-
The resulting call paths are:
|
|
22
|
-
|
|
23
|
-
```text
|
|
24
|
-
Immediate connected read/export/action
|
|
25
|
-
MCP client or dashboard
|
|
26
|
-
-> MCP Scraper main API
|
|
27
|
-
-> Connected Services Control Plane
|
|
28
|
-
-> Nango
|
|
29
|
-
-> provider
|
|
30
|
-
|
|
31
|
-
Scheduled connected work
|
|
32
|
-
Inngest occurrence
|
|
33
|
-
-> MCP Scraper Scheduler
|
|
34
|
-
-> shared integration module / Connected Services Control Plane
|
|
35
|
-
-> Nango
|
|
36
|
-
-> provider
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
The scheduler remains necessary only for recurring jobs, leases, checkpoints, deterministic sync, and scheduled-agent execution. It is not required for one-time Search Console analysis.
|
|
40
|
-
|
|
41
|
-
## Incident statement
|
|
42
|
-
|
|
43
|
-
On 2026-07-16, the tenant Search Console connection had all of the following stored metadata:
|
|
44
|
-
|
|
45
|
-
- provider key `google-search-console`;
|
|
46
|
-
- connection status `connected`;
|
|
47
|
-
- `reconnectRequired: false`;
|
|
48
|
-
- a non-empty read-tool inventory;
|
|
49
|
-
- the expected Search Console connection reference.
|
|
50
|
-
|
|
51
|
-
Despite that metadata, two `list-sites` attempts and a bounded 90-day `search_console_performance` export failed with retryable HTTP 503 responses:
|
|
52
|
-
|
|
53
|
-
```json
|
|
54
|
-
{
|
|
55
|
-
"code": "connection_transport_unavailable",
|
|
56
|
-
"status": 503,
|
|
57
|
-
"retryable": true,
|
|
58
|
-
"message": "The service connection transport is temporarily unavailable."
|
|
59
|
-
}
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Public scraper operations such as SERP search and URL extraction remained healthy. The failure was therefore isolated to the connected-service execution path, not the root MCP Scraper transport.
|
|
63
|
-
|
|
64
|
-
The production Integrations page also displayed the connection as `Connected · Ready for sync`. Its drawer exposed `Refresh access` but no `Disconnect` control. The deployed frontend bundle rendered Disconnect only for `remote_mcp` connections. Current local source removes that transport restriction, proving a production bundle/source mismatch.
|
|
65
|
-
|
|
66
|
-
## Root causes
|
|
67
|
-
|
|
68
|
-
### R1. Stored lifecycle state is presented as runtime health
|
|
69
|
-
|
|
70
|
-
`Connected` currently means that a tenant-owned connection row exists, is marked active, and is not flagged for reauthorization. It does not mean that a provider call has succeeded recently.
|
|
71
|
-
|
|
72
|
-
The frontend derives readiness from stored status and approved-tool inventory. It does not distinguish:
|
|
73
|
-
|
|
74
|
-
- OAuth credential lifecycle;
|
|
75
|
-
- control-plane availability;
|
|
76
|
-
- Nango MCP availability;
|
|
77
|
-
- live provider authorization;
|
|
78
|
-
- provider rate limiting;
|
|
79
|
-
- last successful operation.
|
|
80
|
-
|
|
81
|
-
### R2. Direct operations depend on the scheduler deployment
|
|
82
|
-
|
|
83
|
-
`mcp-scraper/src/api/nango-control.ts` defaults `NANGO_CONTROL_URL` to `https://mcp-scraper-scheduler.vercel.app`. The main API forwards connect, reconnect, disconnect, list, describe, read, action, binding, and export-page operations to internal routes in that deployment.
|
|
84
|
-
|
|
85
|
-
The scheduler deployment therefore acts as both:
|
|
86
|
-
|
|
87
|
-
1. a recurring-work scheduler; and
|
|
88
|
-
2. the Nango integration gateway for all direct operations.
|
|
89
|
-
|
|
90
|
-
The second role is real but unnamed, hidden from the product model, and coupled to scheduler runtime health.
|
|
91
|
-
|
|
92
|
-
### R3. Frontend and backend releases can drift
|
|
93
|
-
|
|
94
|
-
The local `public/app.jsx` and generated `public/app.js` expose Disconnect for every connection transport. The deployed bundle contains the earlier `remote_mcp`-only condition. The release flow does not currently fail when the public bundle, server routes, and production deployment do not share the intended connection-management contract.
|
|
95
|
-
|
|
96
|
-
### R4. Provider policy is duplicated
|
|
97
|
-
|
|
98
|
-
Read/action allowlists and connection-sync requirements exist across the main and scheduler repositories. Comments instruct maintainers to keep the copies aligned, but the contract is not generated from one canonical source. Tool count and readiness can therefore drift across the UI, root MCP, scheduler, and live provider inventory.
|
|
99
|
-
|
|
100
|
-
## Goals
|
|
101
|
-
|
|
102
|
-
1. Make one-time connected-service reads independent of scheduler deployment health.
|
|
103
|
-
2. Preserve Nango as the OAuth credential and token-refresh boundary.
|
|
104
|
-
3. Preserve the existing tenant-owned connection references and Postgres records.
|
|
105
|
-
4. Report credential state and operational state separately.
|
|
106
|
-
5. Never label a connection `Ready` solely because a row and allowlist exist.
|
|
107
|
-
6. Provide reliable connect, reconnect, test, and disconnect controls for Nango and remote-MCP connections.
|
|
108
|
-
7. Keep billing, action safety, tenant isolation, audit, and idempotency intact.
|
|
109
|
-
8. Give operators request-level evidence across the main API, control plane, Nango, and provider.
|
|
110
|
-
9. Prevent source/bundle/deployment contract drift.
|
|
111
|
-
10. Cut over without interrupting existing schedules or requiring customers to reconnect healthy accounts.
|
|
112
|
-
|
|
113
|
-
## Non-goals
|
|
114
|
-
|
|
115
|
-
- Replacing Nango as the OAuth store.
|
|
116
|
-
- Changing the approved $3-per-active-Nango-account commercial contract.
|
|
117
|
-
- Changing connected function, Proxy, or compute rates.
|
|
118
|
-
- Moving scheduled occurrence, lease, checkpoint, or sync execution into the main API.
|
|
119
|
-
- Exposing provider credentials or Nango connection IDs to browsers or MCP clients.
|
|
120
|
-
- Renaming existing Postgres tables during the first cutover.
|
|
121
|
-
- Building a provider-specific Search Console agent inside the control plane.
|
|
122
|
-
- Treating health checks as proof that every accessible Search Console property has data.
|
|
123
|
-
|
|
124
|
-
## Terminology
|
|
125
|
-
|
|
126
|
-
| Term | Meaning |
|
|
127
|
-
|---|---|
|
|
128
|
-
| Connection lifecycle | Whether an OAuth/remote-MCP credential record exists, is active, requires reauthorization, is pending deletion, or is deleted |
|
|
129
|
-
| Operational health | Whether the system has recently reached the connection transport and provider successfully |
|
|
130
|
-
| Control plane | Tenant routing, live tool discovery, provider execution, lifecycle mutation, health evidence, audit, and metering |
|
|
131
|
-
| Scheduler | Recurring occurrence ownership, leases, background execution, checkpoints, and scheduled receipts |
|
|
132
|
-
| Main API | Public authenticated REST/MCP facade, user entitlement enforcement, artifact delivery, and authoritative Credit ledger |
|
|
133
|
-
| Provider contract | Canonical source-controlled classification, required scopes/features, safe probes, action policy, and sync requirements |
|
|
134
|
-
|
|
135
|
-
## Current architecture
|
|
136
|
-
|
|
137
|
-
### Main API responsibilities today
|
|
138
|
-
|
|
139
|
-
The main API currently owns:
|
|
140
|
-
|
|
141
|
-
- customer/API-key authentication;
|
|
142
|
-
- plan gating;
|
|
143
|
-
- public `/schedule-connections/*` routes;
|
|
144
|
-
- root MCP bridge tools;
|
|
145
|
-
- bounded export orchestration and private artifact delivery;
|
|
146
|
-
- connected-account quantity reconciliation;
|
|
147
|
-
- authoritative Credit debits.
|
|
148
|
-
|
|
149
|
-
For Nango connections, it does not execute provider tools itself. It calls `controlRequest()` in `src/api/nango-control.ts`, using `NANGO_CONTROL_URL` and `SCHEDULE_INTEGRATIONS_SECRET`.
|
|
150
|
-
|
|
151
|
-
### Scheduler responsibilities today
|
|
152
|
-
|
|
153
|
-
The scheduler repository currently owns:
|
|
154
|
-
|
|
155
|
-
- `mem_external_connections` and connection-related Postgres tables;
|
|
156
|
-
- connection-to-schedule bindings;
|
|
157
|
-
- Nango connect and reconnect sessions;
|
|
158
|
-
- deletion and action switches;
|
|
159
|
-
- provider policy maps;
|
|
160
|
-
- live Nango MCP discovery;
|
|
161
|
-
- direct read and action execution;
|
|
162
|
-
- connected-data export pages;
|
|
163
|
-
- Nango usage metering and action audit;
|
|
164
|
-
- deterministic scheduled sync and agent-mode scheduled execution.
|
|
165
|
-
|
|
166
|
-
### Credential boundary
|
|
167
|
-
|
|
168
|
-
Nango owns provider credentials and token refresh. Postgres stores tenant identity, provider key, Nango routing identifier, display metadata, lifecycle state, action switch, table/vault references, and timestamps. It intentionally stores no OAuth token.
|
|
169
|
-
|
|
170
|
-
This boundary remains correct and must be preserved.
|
|
171
|
-
|
|
172
|
-
## Target architecture
|
|
173
|
-
|
|
174
|
-
### Component boundaries
|
|
175
|
-
|
|
176
|
-
| Component | Owns | Must not own |
|
|
177
|
-
|---|---|---|
|
|
178
|
-
| MCP Scraper main API | Public auth, entitlement checks, stable REST/MCP facade, authoritative Credit ledger, private artifact authorization, aggregate export response | Provider tokens, Nango secret, scheduler leases |
|
|
179
|
-
| Connected Services Control Plane | Connection rows, Nango credential routing, provider contracts, live discovery, reads/actions, export pages, lifecycle operations, operational health evidence, audit, connected-usage receipts | Public customer authentication, model orchestration, schedule occurrence ownership |
|
|
180
|
-
| MCP Scraper Scheduler | Recurrence, leases, execution budgets, deterministic sync, checkpoints, scheduled-agent runs, run receipts | Public connection-management facade, independent provider policy copies |
|
|
181
|
-
| Nango | OAuth consent, encrypted provider credentials, refresh, MCP/Proxy/functions | Product tenancy, product billing, schedule state |
|
|
182
|
-
| Main Credit ledger | Authoritative balance, idempotent debit/credit receipts | Connection credentials, provider payloads |
|
|
183
|
-
| Private artifact storage | Owner-scoped JSONL and export artifacts | Provider credentials, authorization decisions |
|
|
184
|
-
|
|
185
|
-
### Deployment topology
|
|
186
|
-
|
|
187
|
-
Deploy the Connected Services Control Plane independently, initially from the scheduler repository:
|
|
188
|
-
|
|
189
|
-
```text
|
|
190
|
-
source repo: mcp-scraper-scheduler
|
|
191
|
-
shared module: src/mastra/integrations/*
|
|
192
|
-
deployment A: integrations.mcpscraper.dev
|
|
193
|
-
deployment B: mcp-scraper-scheduler.vercel.app
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
Deployment A exposes only internal integration-control routes and health endpoints. Deployment B exposes scheduled workflow routes and workers. Both import the same provider contracts, connection repository, Nango client, usage metering, and error mapping from the shared module.
|
|
197
|
-
|
|
198
|
-
This is preferable to copying the code into the main repository because:
|
|
199
|
-
|
|
200
|
-
- the Nango secret remains outside the public API deployment;
|
|
201
|
-
- the existing Postgres connection repository remains authoritative;
|
|
202
|
-
- scheduler and direct operations consume the same code rather than two policy copies;
|
|
203
|
-
- cutover needs no credential or connection-row migration.
|
|
204
|
-
|
|
205
|
-
A future repository extraction is allowed only after the shared module and deployment boundary are stable. It is not part of this release.
|
|
206
|
-
|
|
207
|
-
## Architectural decisions
|
|
208
|
-
|
|
209
|
-
### D1. Separate lifecycle state from operational health
|
|
210
|
-
|
|
211
|
-
Every public connection summary must expose both:
|
|
212
|
-
|
|
213
|
-
```ts
|
|
214
|
-
type ConnectionLifecycleStatus =
|
|
215
|
-
| 'pending_oauth'
|
|
216
|
-
| 'connected'
|
|
217
|
-
| 'reauth_required'
|
|
218
|
-
| 'deleting'
|
|
219
|
-
| 'disconnected'
|
|
220
|
-
|
|
221
|
-
type ConnectionOperationalStatus =
|
|
222
|
-
| 'unknown'
|
|
223
|
-
| 'available'
|
|
224
|
-
| 'temporarily_unavailable'
|
|
225
|
-
| 'rate_limited'
|
|
226
|
-
| 'permission_limited'
|
|
227
|
-
| 'provider_error'
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
`connected` is never rendered as `Ready` unless recent operational evidence supports it. A connection that has never been used is `Connected · Not tested`.
|
|
231
|
-
|
|
232
|
-
### D2. Health is evidence from operations, not a page-load side effect
|
|
233
|
-
|
|
234
|
-
Loading the Integrations page must not call every provider. That would create latency, cost, rate pressure, and noisy failures.
|
|
235
|
-
|
|
236
|
-
Operational health is updated by:
|
|
237
|
-
|
|
238
|
-
- successful or failed real reads, actions, descriptions, exports, and scheduled syncs;
|
|
239
|
-
- one provider-specific verification after a successful connect/reconnect callback;
|
|
240
|
-
- an explicit user-triggered Test connection operation.
|
|
241
|
-
|
|
242
|
-
The UI displays the timestamp and source of the latest evidence. Evidence expires to `unknown` after a provider-contract TTL, default six hours for ordinary connections and one hour after a failure.
|
|
243
|
-
|
|
244
|
-
### D3. A 503 is not a reconnect signal
|
|
245
|
-
|
|
246
|
-
Only authentication/authorization evidence can set `reauth_required`, such as a confirmed provider 401, invalid grant, revoked token, or Nango auth-error state.
|
|
247
|
-
|
|
248
|
-
Transport 5xx, Nango MCP discovery failure, billing-control unavailability, and timeouts set operational state but do not change credential lifecycle. The UI must offer Retry/Test, not Reconnect, for those errors.
|
|
249
|
-
|
|
250
|
-
### D4. Provider contracts have one canonical source
|
|
251
|
-
|
|
252
|
-
Create one versioned provider-contract module in the scheduler repository and generate projections for the main repository.
|
|
253
|
-
|
|
254
|
-
Each provider contract contains:
|
|
255
|
-
|
|
256
|
-
```ts
|
|
257
|
-
interface ConnectedProviderContract {
|
|
258
|
-
providerConfigKey: string
|
|
259
|
-
reads: readonly string[]
|
|
260
|
-
actions: readonly string[]
|
|
261
|
-
adminDenylist: readonly string[]
|
|
262
|
-
requiredPermissionsByTool: Record<string, readonly string[]>
|
|
263
|
-
requiredFeaturesByTool: Record<string, readonly string[]>
|
|
264
|
-
sync?: {
|
|
265
|
-
requiredTools: readonly string[]
|
|
266
|
-
optionalTools: readonly string[]
|
|
267
|
-
}
|
|
268
|
-
healthProbe?: {
|
|
269
|
-
tool: string
|
|
270
|
-
args: Record<string, unknown>
|
|
271
|
-
successPredicate: string
|
|
272
|
-
ttlSeconds: number
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
The generated JSON projection includes a schema version and SHA-256 hash. Main API builds fail when its generated projection differs from the scheduler source.
|
|
278
|
-
|
|
279
|
-
### D5. Existing public routes remain compatible during migration
|
|
280
|
-
|
|
281
|
-
Introduce clearer public routes under `/integrations` while retaining `/schedule-connections` aliases for at least one published MCP/package compatibility window.
|
|
282
|
-
|
|
283
|
-
New clients use:
|
|
284
|
-
|
|
285
|
-
```text
|
|
286
|
-
GET /integrations
|
|
287
|
-
POST /integrations/connect-session
|
|
288
|
-
POST /integrations/:id/reconnect-session
|
|
289
|
-
POST /integrations/:id/test
|
|
290
|
-
DELETE /integrations/:id
|
|
291
|
-
POST /integrations/:id/actions-enabled
|
|
292
|
-
POST /integrations/:id/read
|
|
293
|
-
POST /integrations/:id/describe
|
|
294
|
-
POST /integrations/:id/action
|
|
295
|
-
POST /integrations/:id/export
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
The root MCP tool names remain stable in this release. Only their descriptions and result schema gain health information.
|
|
299
|
-
|
|
300
|
-
### D6. Internal control calls are authenticated and traceable
|
|
301
|
-
|
|
302
|
-
Replace the generic shared-bearer-only contract with signed service requests:
|
|
303
|
-
|
|
304
|
-
- service identity;
|
|
305
|
-
- request ID;
|
|
306
|
-
- issued-at timestamp;
|
|
307
|
-
- short expiry;
|
|
308
|
-
- method and path;
|
|
309
|
-
- SHA-256 body digest;
|
|
310
|
-
- HMAC signature using a rotatable internal secret.
|
|
311
|
-
|
|
312
|
-
Accept the old bearer secret during cutover only. Never forward customer-provided identity headers directly. The main API derives tenant identity from its authenticated user and signs that identity into the internal request body.
|
|
313
|
-
|
|
314
|
-
### D7. Direct and scheduled paths share execution code
|
|
315
|
-
|
|
316
|
-
The control plane and scheduler import the same functions for:
|
|
317
|
-
|
|
318
|
-
- connection ownership resolution;
|
|
319
|
-
- provider-policy intersection;
|
|
320
|
-
- Nango client creation;
|
|
321
|
-
- schema discovery;
|
|
322
|
-
- permission/feature verification;
|
|
323
|
-
- read/action execution;
|
|
324
|
-
- export page traversal;
|
|
325
|
-
- usage measurement and receipts;
|
|
326
|
-
- error normalization;
|
|
327
|
-
- operational-health recording.
|
|
328
|
-
|
|
329
|
-
Scheduled execution adds schedule grants and occurrence context around the shared executor. It does not maintain a separate provider implementation.
|
|
330
|
-
|
|
331
|
-
### D8. Disconnect is a first-class lifecycle operation
|
|
332
|
-
|
|
333
|
-
Disconnect must be visible for every tenant-owned connection transport.
|
|
334
|
-
|
|
335
|
-
The operation is ordered as follows:
|
|
336
|
-
|
|
337
|
-
1. authenticate the tenant and resolve ownership;
|
|
338
|
-
2. create an idempotent deletion receipt and mark lifecycle `deleting`;
|
|
339
|
-
3. revoke/delete the credential in Nango or the remote-MCP store;
|
|
340
|
-
4. delete the local connection row in a transaction;
|
|
341
|
-
5. cascade schedule bindings, checkpoints, synced records, and action audit according to retention policy;
|
|
342
|
-
6. return the remaining billable Nango quantity;
|
|
343
|
-
7. reconcile Stripe quantity from the main API;
|
|
344
|
-
8. mark the deletion receipt complete.
|
|
345
|
-
|
|
346
|
-
If credential deletion is unavailable, retain the local row in `deleting` and return a retryable error. Do not silently delete local routing while leaving an active credential. An administrative force-local-delete path is separate, audited, and unavailable to ordinary users.
|
|
347
|
-
|
|
348
|
-
### D9. Reconnect updates the same logical connection
|
|
349
|
-
|
|
350
|
-
Reconnect must preserve the public connection reference, schedule bindings, billing identity, table/vault associations, and audit history. It refreshes or replaces the upstream Nango credential routing identifier only if Nango requires it.
|
|
351
|
-
|
|
352
|
-
Reconnect must not create an additional billable connection for the same logical account. Duplicate detection uses tenant, provider key, and upstream connection identity inside the control plane.
|
|
353
|
-
|
|
354
|
-
### D10. Reads retry safely; actions require idempotency
|
|
355
|
-
|
|
356
|
-
The control plane may retry read-only discovery/read/export-page operations for retryable transport failures with bounded exponential backoff and jitter:
|
|
357
|
-
|
|
358
|
-
- maximum two retries after the original attempt;
|
|
359
|
-
- total retry budget no greater than the public request deadline;
|
|
360
|
-
- respect provider `Retry-After`;
|
|
361
|
-
- do not retry validation, permission, or authentication failures.
|
|
362
|
-
|
|
363
|
-
Actions are never automatically replayed after an ambiguous provider outcome. They retain the existing idempotency/outcome-unknown contract.
|
|
364
|
-
|
|
365
|
-
### D11. Release identity is part of the contract
|
|
366
|
-
|
|
367
|
-
Every deployment returns:
|
|
368
|
-
|
|
369
|
-
```json
|
|
370
|
-
{
|
|
371
|
-
"service": "main-api | integrations-control | scheduler",
|
|
372
|
-
"releaseId": "git-sha-or-build-id",
|
|
373
|
-
"providerContractVersion": "...",
|
|
374
|
-
"providerContractHash": "...",
|
|
375
|
-
"builtAt": "RFC3339"
|
|
376
|
-
}
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
The frontend embeds its release ID. Production verification fails if the UI bundle lacks a control promised by the server contract or if provider-contract hashes differ.
|
|
380
|
-
|
|
381
|
-
### D12. Search Console uses a bounded safe diagnostic
|
|
382
|
-
|
|
383
|
-
The Search Console provider contract defines:
|
|
384
|
-
|
|
385
|
-
```json
|
|
386
|
-
{
|
|
387
|
-
"tool": "list-sites",
|
|
388
|
-
"args": {},
|
|
389
|
-
"successPredicate": "result contains a valid site collection, including an empty collection",
|
|
390
|
-
"ttlSeconds": 21600
|
|
391
|
-
}
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
A successful probe proves that MCP Scraper reached Nango, Google accepted the credential, and Search Console returned a valid property collection. It does not prove that a named property exists or has performance rows.
|
|
395
|
-
|
|
396
|
-
After the generic probe, the Geny's Flowers workflow must separately:
|
|
397
|
-
|
|
398
|
-
1. match an accessible property by normalized domain/site URL;
|
|
399
|
-
2. report the exact selected property;
|
|
400
|
-
3. run a bounded Search Analytics query;
|
|
401
|
-
4. only then begin page extraction, SERP, PAA, or strategy synthesis.
|
|
402
|
-
|
|
403
|
-
## Public connection representation
|
|
404
|
-
|
|
405
|
-
The connection summary becomes:
|
|
406
|
-
|
|
407
|
-
```ts
|
|
408
|
-
interface PublicServiceConnection {
|
|
409
|
-
connectionId: string
|
|
410
|
-
providerConfigKey: string
|
|
411
|
-
provider: string | null
|
|
412
|
-
label: string
|
|
413
|
-
transport: 'nango' | 'remote_mcp'
|
|
414
|
-
|
|
415
|
-
lifecycleStatus: ConnectionLifecycleStatus
|
|
416
|
-
reconnectRequired: boolean
|
|
417
|
-
|
|
418
|
-
operationalStatus: ConnectionOperationalStatus
|
|
419
|
-
lastCheckedAt: string | null
|
|
420
|
-
lastSuccessfulCallAt: string | null
|
|
421
|
-
lastFailureAt: string | null
|
|
422
|
-
lastFailureCode: string | null
|
|
423
|
-
lastFailureRetryable: boolean | null
|
|
424
|
-
healthEvidenceSource: 'connect_probe' | 'manual_test' | 'direct_read' | 'action' | 'export' | 'scheduled_sync' | null
|
|
425
|
-
|
|
426
|
-
actionsEnabled: boolean
|
|
427
|
-
readTools: string[]
|
|
428
|
-
actionTools: string[]
|
|
429
|
-
toolCapabilities: ToolCapability[]
|
|
430
|
-
tableName: string | null
|
|
431
|
-
createdAt: string | null
|
|
432
|
-
updatedAt: string | null
|
|
433
|
-
}
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
Compatibility fields `status` and `reconnectRequired` remain for one version. `status` maps only to lifecycle and must be documented as such.
|
|
437
|
-
|
|
438
|
-
## Error contract
|
|
439
|
-
|
|
440
|
-
All surfaces use the same safe taxonomy:
|
|
441
|
-
|
|
442
|
-
| Code | HTTP | Lifecycle effect | Operational effect | User action |
|
|
443
|
-
|---|---:|---|---|---|
|
|
444
|
-
| `connection_not_found` | 404 | none | none | Select another connection |
|
|
445
|
-
| `connection_inactive` | 409 | reauth when confirmed | unknown | Reconnect |
|
|
446
|
-
| `provider_auth_revoked` | 401/409 | `reauth_required` | permission limited | Reconnect |
|
|
447
|
-
| `missing_provider_permission` | 403 | connected | permission limited | Reconnect with expanded scope when supported |
|
|
448
|
-
| `connection_transport_unavailable` | 503 | connected | temporarily unavailable | Retry/test later; operator checks control plane |
|
|
449
|
-
| `tool_discovery_failed` | 502 | connected | provider error | Retry; inspect Nango discovery |
|
|
450
|
-
| `upstream_rate_limited` | 429 | connected | rate limited | Retry after returned time |
|
|
451
|
-
| `provider_error` | 502 | connected | provider error | Retry or inspect provider status |
|
|
452
|
-
| `control_plane_not_configured` | 503 | connected | temporarily unavailable | Operator configuration fix |
|
|
453
|
-
| `billing_authorization_unavailable` | 503 | connected | temporarily unavailable | Retry; do not reconnect |
|
|
454
|
-
| `actions_disabled` | 403 | connected | unchanged | Explicitly enable actions |
|
|
455
|
-
| `action_outcome_unknown` | 409 | connected | unchanged | Verify provider state before another action |
|
|
456
|
-
|
|
457
|
-
Every error includes `requestId`, `retryable`, `sourceLayer`, and a safe message. Provider tokens, raw payloads, customer prompts, and secrets never appear in errors.
|
|
458
|
-
|
|
459
|
-
## Persistence changes
|
|
460
|
-
|
|
461
|
-
### Extend connection records
|
|
462
|
-
|
|
463
|
-
Add to `mem_external_connections`:
|
|
464
|
-
|
|
465
|
-
```sql
|
|
466
|
-
ALTER TABLE mem_external_connections
|
|
467
|
-
ADD COLUMN IF NOT EXISTS lifecycle_status TEXT NOT NULL DEFAULT 'connected',
|
|
468
|
-
ADD COLUMN IF NOT EXISTS operational_status TEXT NOT NULL DEFAULT 'unknown',
|
|
469
|
-
ADD COLUMN IF NOT EXISTS last_checked_at TIMESTAMPTZ,
|
|
470
|
-
ADD COLUMN IF NOT EXISTS last_successful_call_at TIMESTAMPTZ,
|
|
471
|
-
ADD COLUMN IF NOT EXISTS last_failure_at TIMESTAMPTZ,
|
|
472
|
-
ADD COLUMN IF NOT EXISTS last_failure_code TEXT,
|
|
473
|
-
ADD COLUMN IF NOT EXISTS last_failure_retryable BOOLEAN,
|
|
474
|
-
ADD COLUMN IF NOT EXISTS health_evidence_source TEXT,
|
|
475
|
-
ADD COLUMN IF NOT EXISTS consecutive_failures INTEGER NOT NULL DEFAULT 0;
|
|
476
|
-
```
|
|
477
|
-
|
|
478
|
-
Keep the existing `status` column during migration. Backfill `lifecycle_status` from it, then make new code write both until every deployed reader supports the new column.
|
|
479
|
-
|
|
480
|
-
### Deletion receipts
|
|
481
|
-
|
|
482
|
-
Add an idempotent receipt table:
|
|
483
|
-
|
|
484
|
-
```sql
|
|
485
|
-
CREATE TABLE IF NOT EXISTS mem_external_connection_deletions (
|
|
486
|
-
idempotency_key TEXT PRIMARY KEY,
|
|
487
|
-
identity TEXT NOT NULL,
|
|
488
|
-
external_connection_id TEXT NOT NULL,
|
|
489
|
-
provider_config_key TEXT NOT NULL,
|
|
490
|
-
status TEXT NOT NULL,
|
|
491
|
-
upstream_deleted_at TIMESTAMPTZ,
|
|
492
|
-
local_deleted_at TIMESTAMPTZ,
|
|
493
|
-
remaining_billable_connections INTEGER,
|
|
494
|
-
error_code TEXT,
|
|
495
|
-
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
496
|
-
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
497
|
-
);
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
The receipt contains no token or provider content.
|
|
501
|
-
|
|
502
|
-
### Health events
|
|
503
|
-
|
|
504
|
-
Persist bounded diagnostic history separately from the summary row:
|
|
505
|
-
|
|
506
|
-
```sql
|
|
507
|
-
CREATE TABLE IF NOT EXISTS mem_external_connection_health_events (
|
|
508
|
-
id TEXT PRIMARY KEY,
|
|
509
|
-
identity TEXT NOT NULL,
|
|
510
|
-
external_connection_id TEXT NOT NULL,
|
|
511
|
-
operation TEXT NOT NULL,
|
|
512
|
-
source_surface TEXT NOT NULL,
|
|
513
|
-
status TEXT NOT NULL,
|
|
514
|
-
error_code TEXT,
|
|
515
|
-
retryable BOOLEAN,
|
|
516
|
-
duration_ms INTEGER,
|
|
517
|
-
request_id TEXT NOT NULL,
|
|
518
|
-
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
519
|
-
);
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
Retain 30 days by default. Never store tool arguments or provider results in this table.
|
|
523
|
-
|
|
524
|
-
## API contracts
|
|
525
|
-
|
|
526
|
-
### `GET /integrations`
|
|
527
|
-
|
|
528
|
-
Returns connection lifecycle, operational evidence, capabilities, billing summary, and control-plane release metadata. It does not probe providers.
|
|
529
|
-
|
|
530
|
-
### `POST /integrations/:id/test`
|
|
531
|
-
|
|
532
|
-
Runs the provider contract's safe diagnostic under normal tenant ownership and usage policy.
|
|
533
|
-
|
|
534
|
-
Response:
|
|
535
|
-
|
|
536
|
-
```json
|
|
537
|
-
{
|
|
538
|
-
"ok": true,
|
|
539
|
-
"connectionId": "...",
|
|
540
|
-
"lifecycleStatus": "connected",
|
|
541
|
-
"operationalStatus": "available",
|
|
542
|
-
"checkedAt": "...",
|
|
543
|
-
"probe": {
|
|
544
|
-
"tool": "list-sites",
|
|
545
|
-
"summary": "Search Console returned 4 accessible properties"
|
|
546
|
-
},
|
|
547
|
-
"usage": {},
|
|
548
|
-
"requestId": "..."
|
|
549
|
-
}
|
|
550
|
-
```
|
|
551
|
-
|
|
552
|
-
The summary is bounded and contains no unnecessary provider data. The operation follows the released connected-usage billing contract. The UI discloses that testing performs one provider operation.
|
|
553
|
-
|
|
554
|
-
### `POST /integrations/:id/read`
|
|
555
|
-
|
|
556
|
-
This is the renamed public facade for the existing generic bridge. The control plane validates tenant ownership, lifecycle, provider policy, live discovery, and arguments before execution.
|
|
557
|
-
|
|
558
|
-
### `DELETE /integrations/:id`
|
|
559
|
-
|
|
560
|
-
Requires a client idempotency key. Returns a deletion receipt. Stripe reconciliation happens in the main API only after the control plane confirms upstream and local deletion.
|
|
561
|
-
|
|
562
|
-
### Internal control-plane endpoints
|
|
563
|
-
|
|
564
|
-
Internal routes move from `/api/internal/nango/*` to `/api/internal/integrations/*`. Old routes remain aliases during cutover.
|
|
565
|
-
|
|
566
|
-
Required internal groups:
|
|
567
|
-
|
|
568
|
-
- catalog and provider-contract metadata;
|
|
569
|
-
- connections list/get;
|
|
570
|
-
- connect/reconnect session;
|
|
571
|
-
- test/health;
|
|
572
|
-
- describe/read/action;
|
|
573
|
-
- export page;
|
|
574
|
-
- action switch;
|
|
575
|
-
- delete;
|
|
576
|
-
- schedule bindings;
|
|
577
|
-
- readiness and release metadata.
|
|
578
|
-
|
|
579
|
-
## Frontend contract
|
|
580
|
-
|
|
581
|
-
### Connection card states
|
|
582
|
-
|
|
583
|
-
| Lifecycle | Operational | Primary badge | Secondary text | Primary recovery action |
|
|
584
|
-
|---|---|---|---|---|
|
|
585
|
-
| connected | available | Available | Last successful check timestamp | Test again |
|
|
586
|
-
| connected | unknown | Connected | Not tested recently | Test connection |
|
|
587
|
-
| connected | temporarily_unavailable | Temporarily unavailable | Safe error and timestamp | Retry test |
|
|
588
|
-
| connected | rate_limited | Rate limited | Retry-after when present | Retry later |
|
|
589
|
-
| connected | permission_limited | Limited access | Missing permissions/features | Refresh access |
|
|
590
|
-
| reauth_required | any | Reconnect required | Authorization expired or revoked | Reconnect |
|
|
591
|
-
| pending_oauth | unknown | Authorization pending | Finish OAuth | Continue authorization |
|
|
592
|
-
| deleting | any | Disconnecting | Safe progress state | Retry deletion when failed |
|
|
593
|
-
|
|
594
|
-
The phrase `Ready for sync` is reserved for a provider that:
|
|
595
|
-
|
|
596
|
-
- is lifecycle connected;
|
|
597
|
-
- has recent operational status available;
|
|
598
|
-
- exposes every required sync tool;
|
|
599
|
-
- has a successful sync policy validation.
|
|
600
|
-
|
|
601
|
-
### Drawer controls
|
|
602
|
-
|
|
603
|
-
Every connection drawer exposes:
|
|
604
|
-
|
|
605
|
-
- Test connection;
|
|
606
|
-
- Refresh access or Reconnect, with wording driven by lifecycle state;
|
|
607
|
-
- Disconnect;
|
|
608
|
-
- action switch when actions exist;
|
|
609
|
-
- last successful call;
|
|
610
|
-
- last failure and safe error code;
|
|
611
|
-
- connection reference;
|
|
612
|
-
- provider-contract version/hash in an operator details disclosure.
|
|
613
|
-
|
|
614
|
-
Disconnect is not conditioned on transport type. It always requires a confirmation describing schedule impact and billing removal.
|
|
615
|
-
|
|
616
|
-
### Failure messaging for this incident
|
|
617
|
-
|
|
618
|
-
For `connection_transport_unavailable`, show:
|
|
619
|
-
|
|
620
|
-
> Your Google authorization is still connected, but MCP Scraper could not reach the connected-service gateway. Retrying OAuth is unlikely to help. Try Test connection again later or contact support with request ID …
|
|
621
|
-
|
|
622
|
-
Do not show `Reconnect required` unless lifecycle evidence supports it.
|
|
623
|
-
|
|
624
|
-
## Root MCP contract
|
|
625
|
-
|
|
626
|
-
Keep these tool names stable:
|
|
627
|
-
|
|
628
|
-
- `list_service_connections`;
|
|
629
|
-
- `describe_service_connection_tool`;
|
|
630
|
-
- `read_service_connection`;
|
|
631
|
-
- `call_service_connection_action`;
|
|
632
|
-
- `export_connected_service_data`.
|
|
633
|
-
|
|
634
|
-
Add:
|
|
635
|
-
|
|
636
|
-
- `test_service_connection` for explicit bounded diagnostics.
|
|
637
|
-
|
|
638
|
-
`list_service_connections` descriptions must state that `lifecycleStatus` and `operationalStatus` are different. `read_service_connection` and export errors must return the unified safe code, retryability, source layer, and request ID.
|
|
639
|
-
|
|
640
|
-
The MCP instructions must stop calling a stored connection `healthy` merely because `reconnectRequired` is false.
|
|
641
|
-
|
|
642
|
-
## Search Console analysis contract
|
|
643
|
-
|
|
644
|
-
A comprehensive Search Console request follows this deterministic opening sequence:
|
|
645
|
-
|
|
646
|
-
1. list tenant connections;
|
|
647
|
-
2. select the exact Search Console connection;
|
|
648
|
-
3. inspect lifecycle and operational evidence;
|
|
649
|
-
4. if unknown or stale, call `test_service_connection`;
|
|
650
|
-
5. call `list-sites` and enumerate accessible properties;
|
|
651
|
-
6. select the requested property by normalized site URL/domain, never by brand-name guess alone;
|
|
652
|
-
7. export or query a declared date range;
|
|
653
|
-
8. validate row coverage and provider warnings;
|
|
654
|
-
9. enrich only the relevant pages/queries with URL extraction, SERP, PAA, Maps, or competitors;
|
|
655
|
-
10. distinguish Search Console facts from public-web inferences in the final report.
|
|
656
|
-
|
|
657
|
-
If steps 4-7 fail, the analysis is blocked rather than silently replaced with public SERP data.
|
|
658
|
-
|
|
659
|
-
## Reliability behavior
|
|
660
|
-
|
|
661
|
-
### Deadlines
|
|
662
|
-
|
|
663
|
-
- catalog/list: 10 seconds;
|
|
664
|
-
- explicit test/read/describe: 20 seconds, including retries;
|
|
665
|
-
- export page: 30 seconds;
|
|
666
|
-
- aggregate export: existing bounded server orchestration deadline with continuation;
|
|
667
|
-
- actions: provider-specific, with idempotency and outcome-unknown protection.
|
|
668
|
-
|
|
669
|
-
### Circuit breaker
|
|
670
|
-
|
|
671
|
-
Track control-plane/Nango transport failures separately from tenant credential failures.
|
|
672
|
-
|
|
673
|
-
Open a short circuit when a deployment-wide threshold is met, for example five retryable transport failures across at least three tenants in one minute. While open:
|
|
674
|
-
|
|
675
|
-
- fail fast with `connection_transport_unavailable`;
|
|
676
|
-
- do not mark individual credentials for reconnect;
|
|
677
|
-
- keep scheduled occurrences retryable without double billing;
|
|
678
|
-
- surface one operator incident rather than many customer-auth incidents.
|
|
679
|
-
|
|
680
|
-
### Scheduled occurrence behavior
|
|
681
|
-
|
|
682
|
-
If the control plane is temporarily unavailable before provider execution begins:
|
|
683
|
-
|
|
684
|
-
- do not consume the fixed scheduled-run charge;
|
|
685
|
-
- release or retry the occurrence using its existing lease/idempotency identity;
|
|
686
|
-
- retain checkpoint state;
|
|
687
|
-
- emit a retryable infrastructure receipt.
|
|
688
|
-
|
|
689
|
-
If provider execution has begun, use the existing billing and partial-result contracts.
|
|
690
|
-
|
|
691
|
-
## Security and privacy
|
|
692
|
-
|
|
693
|
-
1. Main API derives identity from authenticated context; browsers cannot choose another identity.
|
|
694
|
-
2. Internal requests are signed, time bounded, and replay protected.
|
|
695
|
-
3. Connection IDs are routing identifiers, not authorization.
|
|
696
|
-
4. Foreign and absent connection IDs return the same public not-found response.
|
|
697
|
-
5. Nango secret and connection routing IDs remain server-only.
|
|
698
|
-
6. Logs contain request IDs, safe codes, provider keys, and durations—not credentials, query payloads, email contents, Search Console rows, or prompts.
|
|
699
|
-
7. Health events contain no provider result bodies.
|
|
700
|
-
8. Disconnect deletes credential routing and cascades bound data according to the published data-deletion policy.
|
|
701
|
-
9. Actions remain gated by tenant ownership, live discovery, source policy, action switch, exact scheduled grant, and idempotency.
|
|
702
|
-
|
|
703
|
-
## Observability
|
|
704
|
-
|
|
705
|
-
### Required request context
|
|
706
|
-
|
|
707
|
-
Propagate one request ID through:
|
|
708
|
-
|
|
709
|
-
```text
|
|
710
|
-
root MCP or dashboard
|
|
711
|
-
-> main API
|
|
712
|
-
-> integration control plane
|
|
713
|
-
-> Nango client/Proxy/function
|
|
714
|
-
-> billing receipt
|
|
715
|
-
-> health event
|
|
716
|
-
```
|
|
717
|
-
|
|
718
|
-
### Metrics
|
|
719
|
-
|
|
720
|
-
Record by provider and operation class:
|
|
721
|
-
|
|
722
|
-
- request count and latency;
|
|
723
|
-
- success/error count;
|
|
724
|
-
- safe error code;
|
|
725
|
-
- retry count;
|
|
726
|
-
- circuit state;
|
|
727
|
-
- live discovery duration;
|
|
728
|
-
- last successful provider operation;
|
|
729
|
-
- lifecycle transitions;
|
|
730
|
-
- deletion receipts pending/failed;
|
|
731
|
-
- provider-contract hash mismatches;
|
|
732
|
-
- main/control/scheduler release mismatch.
|
|
733
|
-
|
|
734
|
-
### Alerts
|
|
735
|
-
|
|
736
|
-
Alert operators when:
|
|
737
|
-
|
|
738
|
-
- control-plane readiness fails;
|
|
739
|
-
- Nango discovery failure rate exceeds threshold;
|
|
740
|
-
- `connection_transport_unavailable` affects multiple tenants;
|
|
741
|
-
- provider-contract hashes differ between deployments;
|
|
742
|
-
- deletion receipts remain incomplete;
|
|
743
|
-
- billing reconciliation differs from active Nango quantity;
|
|
744
|
-
- a deployment serves a frontend bundle without the server's required controls.
|
|
745
|
-
|
|
746
|
-
## Health endpoints
|
|
747
|
-
|
|
748
|
-
The control-plane deployment exposes internal authenticated endpoints:
|
|
749
|
-
|
|
750
|
-
```text
|
|
751
|
-
GET /api/internal/integrations/health/live
|
|
752
|
-
GET /api/internal/integrations/health/ready
|
|
753
|
-
GET /api/internal/integrations/release
|
|
754
|
-
```
|
|
755
|
-
|
|
756
|
-
`live` proves the process can answer. `ready` verifies required configuration and bounded dependencies without enumerating customer connections:
|
|
757
|
-
|
|
758
|
-
- Postgres connectivity;
|
|
759
|
-
- Nango configuration present;
|
|
760
|
-
- provider-contract load/hash;
|
|
761
|
-
- main billing callback configuration;
|
|
762
|
-
- schema migration compatibility.
|
|
763
|
-
|
|
764
|
-
Nango network availability may be reported as a dependency state without making readiness permanently fail during a short upstream incident. The response distinguishes `ready`, `degraded`, and `not_ready`.
|
|
765
|
-
|
|
766
|
-
## Implementation plan
|
|
767
|
-
|
|
768
|
-
### Phase 0 — production repair and truthful UI
|
|
769
|
-
|
|
770
|
-
Purpose: restore current functionality before architectural extraction.
|
|
771
|
-
|
|
772
|
-
1. Inspect scheduler production logs for the failed request IDs.
|
|
773
|
-
2. Verify `NANGO_SECRET_KEY`, `NANGO_MCP_URL`, `SCHEDULE_INTEGRATIONS_SECRET`, Postgres, and billing callback configuration in the scheduler deployment without exposing values.
|
|
774
|
-
3. Call scheduler readiness and a sanitized Nango discovery smoke.
|
|
775
|
-
4. Restore the existing transport and prove Search Console `list-sites`.
|
|
776
|
-
5. Deploy the current main frontend bundle so Nango connections show Disconnect.
|
|
777
|
-
6. Replace `Connected · Ready for sync` with lifecycle-only wording until operational health fields ship.
|
|
778
|
-
7. Add a production smoke that opens the Search Console drawer and asserts Refresh/Test/Disconnect controls.
|
|
779
|
-
|
|
780
|
-
Exit gate:
|
|
781
|
-
|
|
782
|
-
- current connection lists Search Console sites;
|
|
783
|
-
- bounded export returns rows or a valid empty/provider-warning result;
|
|
784
|
-
- live UI exposes Disconnect;
|
|
785
|
-
- no customer credential is deleted or recreated solely to repair the transport.
|
|
786
|
-
|
|
787
|
-
### Phase 1 — extract the shared integration module
|
|
788
|
-
|
|
789
|
-
Move connection-control code in `mcp-scraper-scheduler` behind stable internal interfaces:
|
|
790
|
-
|
|
791
|
-
```text
|
|
792
|
-
src/mastra/integrations/contracts/
|
|
793
|
-
src/mastra/integrations/repository/
|
|
794
|
-
src/mastra/integrations/execution/
|
|
795
|
-
src/mastra/integrations/health/
|
|
796
|
-
src/mastra/integrations/http/
|
|
797
|
-
src/mastra/integrations/billing/
|
|
798
|
-
```
|
|
799
|
-
|
|
800
|
-
Keep compatibility re-exports while scheduler imports migrate.
|
|
801
|
-
|
|
802
|
-
Exit gate:
|
|
803
|
-
|
|
804
|
-
- scheduler tests pass using the extracted module;
|
|
805
|
-
- direct read/action/export route tests pass without importing schedule execution code;
|
|
806
|
-
- one canonical provider contract generates all projections.
|
|
807
|
-
|
|
808
|
-
### Phase 2 — deploy the independent control plane
|
|
809
|
-
|
|
810
|
-
1. Create the separate Vercel project and domain.
|
|
811
|
-
2. Configure its Postgres, Nango, internal signing, billing callback, and runtime settings.
|
|
812
|
-
3. Apply additive schema migrations.
|
|
813
|
-
4. Deploy internal route aliases and health/release endpoints.
|
|
814
|
-
5. Run sanitized catalog, list, test, read, describe, export-page, reconnect-session creation, and non-destructive deletion-fixture tests in staging.
|
|
815
|
-
|
|
816
|
-
Exit gate:
|
|
817
|
-
|
|
818
|
-
- independent readiness is green;
|
|
819
|
-
- its provider-contract hash matches the scheduler build;
|
|
820
|
-
- it can read a staging Search Console fixture without the scheduler deployment.
|
|
821
|
-
|
|
822
|
-
### Phase 3 — main API shadow and cutover
|
|
823
|
-
|
|
824
|
-
Add environment variables:
|
|
825
|
-
|
|
826
|
-
```text
|
|
827
|
-
INTEGRATIONS_CONTROL_URL
|
|
828
|
-
INTEGRATIONS_CONTROL_KEY_CURRENT
|
|
829
|
-
INTEGRATIONS_CONTROL_KEY_PREVIOUS
|
|
830
|
-
INTEGRATIONS_CONTROL_MODE=legacy|shadow|primary
|
|
831
|
-
```
|
|
832
|
-
|
|
833
|
-
Modes:
|
|
834
|
-
|
|
835
|
-
- `legacy`: use `NANGO_CONTROL_URL` scheduler path;
|
|
836
|
-
- `shadow`: execute safe catalog/list/health comparisons against both; only legacy result is customer-visible; never shadow actions or deletes;
|
|
837
|
-
- `primary`: use the independent control plane, retain legacy fallback only for explicitly allowed read-only operations during the rollback window.
|
|
838
|
-
|
|
839
|
-
Do not automatically fail over actions, connect/reconnect sessions, or deletion across services because duplicate side effects are possible.
|
|
840
|
-
|
|
841
|
-
Exit gate:
|
|
842
|
-
|
|
843
|
-
- shadow comparisons meet parity thresholds;
|
|
844
|
-
- Search Console list/read/export works with scheduler deployment disabled in staging;
|
|
845
|
-
- request IDs and usage receipts reconcile.
|
|
846
|
-
|
|
847
|
-
### Phase 4 — health state and frontend
|
|
848
|
-
|
|
849
|
-
1. Ship additive health columns and event repository.
|
|
850
|
-
2. Record health on every execution path.
|
|
851
|
-
3. Add public health fields and `test_service_connection`.
|
|
852
|
-
4. update frontend states, timestamps, controls, and failure guidance;
|
|
853
|
-
5. update MCP schemas, descriptions, generated manifest, MCPB, docs, and SDK types.
|
|
854
|
-
|
|
855
|
-
Exit gate:
|
|
856
|
-
|
|
857
|
-
- a simulated control-plane 503 shows `Connected · Temporarily unavailable`;
|
|
858
|
-
- it does not show Reconnect required;
|
|
859
|
-
- a successful test transitions to Available;
|
|
860
|
-
- a confirmed revoked grant transitions to Reconnect required.
|
|
861
|
-
|
|
862
|
-
### Phase 5 — scheduler cutover and cleanup
|
|
863
|
-
|
|
864
|
-
1. Make scheduled paths consume the extracted shared integration module.
|
|
865
|
-
2. Remove direct public-operation responsibility from the scheduler deployment.
|
|
866
|
-
3. Keep old internal route aliases for one release window with deprecation metrics.
|
|
867
|
-
4. Remove the main API default fallback to `mcp-scraper-scheduler.vercel.app` after production proof.
|
|
868
|
-
5. Rename public code concepts from `scheduleConnection*` to `serviceConnection*` or `integration*`, retaining compatibility exports where required.
|
|
869
|
-
|
|
870
|
-
Exit gate:
|
|
871
|
-
|
|
872
|
-
- stopping the scheduler web deployment does not break direct provider reads;
|
|
873
|
-
- stopping the control plane blocks direct and scheduled provider execution with the same safe operational error, without corrupting schedule state;
|
|
874
|
-
- no old route traffic remains before alias removal.
|
|
875
|
-
|
|
876
|
-
## Repository task breakdown
|
|
877
|
-
|
|
878
|
-
### `mcp-scraper-scheduler`
|
|
879
|
-
|
|
880
|
-
- IC-01: Extract canonical provider contracts and generated manifest.
|
|
881
|
-
- IC-02: Extract tenant connection repository and lifecycle state transitions.
|
|
882
|
-
- IC-03: Extract provider executor for discovery/read/action/export.
|
|
883
|
-
- IC-04: Add operational-health repository and events.
|
|
884
|
-
- IC-05: Add idempotent deletion workflow and receipts.
|
|
885
|
-
- IC-06: Add signed internal HTTP verification.
|
|
886
|
-
- IC-07: Add independent control-plane entrypoint and health/release routes.
|
|
887
|
-
- IC-08: Migrate scheduled agent and deterministic sync to shared executor.
|
|
888
|
-
- IC-09: Add circuit breaker, retry policy, and safe error mapping.
|
|
889
|
-
- IC-10: Add staging fixtures and integration contract tests.
|
|
890
|
-
|
|
891
|
-
### `mcp-scraper`
|
|
892
|
-
|
|
893
|
-
- IC-11: Add integration-control client with legacy/shadow/primary modes.
|
|
894
|
-
- IC-12: Add `/integrations` routes and `/schedule-connections` aliases.
|
|
895
|
-
- IC-13: Extend connection schemas with lifecycle and operational health.
|
|
896
|
-
- IC-14: Add `test_service_connection` MCP tool.
|
|
897
|
-
- IC-15: Update Search Console tool guidance and analysis opening sequence.
|
|
898
|
-
- IC-16: Make Disconnect transport-neutral and add Test connection UI.
|
|
899
|
-
- IC-17: Replace optimistic readiness badges with evidence-backed states.
|
|
900
|
-
- IC-18: Add release/contract-hash parity checks.
|
|
901
|
-
- IC-19: Update generated tool manifests, MCPB, docs, legal deletion wording, and SDK-facing schemas.
|
|
902
|
-
- IC-20: Add production smoke and rollback verification.
|
|
903
|
-
|
|
904
|
-
## Test plan
|
|
905
|
-
|
|
906
|
-
### Unit tests
|
|
907
|
-
|
|
908
|
-
- lifecycle-to-operational state mapping;
|
|
909
|
-
- safe error taxonomy;
|
|
910
|
-
- provider-contract generation and hash stability;
|
|
911
|
-
- Search Console safe probe definition;
|
|
912
|
-
- health TTL expiry;
|
|
913
|
-
- retry eligibility and deadline budget;
|
|
914
|
-
- circuit-breaker transitions;
|
|
915
|
-
- disconnect idempotency;
|
|
916
|
-
- reconnect preserves logical connection identity;
|
|
917
|
-
- billing quantity only changes after confirmed deletion;
|
|
918
|
-
- HMAC request signing, expiry, replay rejection, and key rotation.
|
|
919
|
-
|
|
920
|
-
### Contract tests
|
|
921
|
-
|
|
922
|
-
- main and control-plane request/response schemas;
|
|
923
|
-
- old and new route aliases are equivalent;
|
|
924
|
-
- root MCP schema includes health fields and request IDs;
|
|
925
|
-
- frontend bundle contains Test, Refresh/Reconnect, and Disconnect for Nango;
|
|
926
|
-
- frontend does not use `reconnectRequired === false` as readiness;
|
|
927
|
-
- provider-contract hashes match across repos/deployments;
|
|
928
|
-
- generated bundle and JSX share the same connection controls;
|
|
929
|
-
- health events never persist arguments or provider bodies.
|
|
930
|
-
|
|
931
|
-
### Integration tests
|
|
932
|
-
|
|
933
|
-
- active connection plus unavailable control plane;
|
|
934
|
-
- invalid internal signature;
|
|
935
|
-
- Nango MCP discovery 503;
|
|
936
|
-
- provider 401/revoked grant;
|
|
937
|
-
- provider 403/missing permission;
|
|
938
|
-
- provider 429 with Retry-After;
|
|
939
|
-
- successful Search Console `list-sites` with zero and multiple properties;
|
|
940
|
-
- Search Analytics valid empty result;
|
|
941
|
-
- aggregate export continuation;
|
|
942
|
-
- delete with Nango success and billing reconcile success;
|
|
943
|
-
- delete with Nango failure retains local state and billing quantity;
|
|
944
|
-
- reconnect does not create duplicate billing quantity;
|
|
945
|
-
- schedule binding survives reconnect;
|
|
946
|
-
- schedule binding cascades on confirmed disconnect.
|
|
947
|
-
|
|
948
|
-
### End-to-end staging tests
|
|
949
|
-
|
|
950
|
-
1. Disable scheduler deployment; direct Search Console test/read/export still passes.
|
|
951
|
-
2. Disable integration control plane; direct calls fail safely and UI shows temporary unavailability.
|
|
952
|
-
3. Restore control plane; Test connection changes operational state to available.
|
|
953
|
-
4. Revoke a staging Google grant; lifecycle becomes reauth required.
|
|
954
|
-
5. Reconnect; the same logical connection and bindings become available.
|
|
955
|
-
6. Disconnect a disposable fixture; Nango credential, local row, bindings, synchronized data, and recurring quantity are removed exactly once.
|
|
956
|
-
7. Run a scheduled Search Console sync through the shared executor.
|
|
957
|
-
8. Verify all request, health, usage, and schedule receipts share traceable IDs.
|
|
958
|
-
|
|
959
|
-
### Production proof
|
|
960
|
-
|
|
961
|
-
For the existing Search Console tenant, collect only sanitized evidence:
|
|
962
|
-
|
|
963
|
-
- main/control/scheduler release IDs;
|
|
964
|
-
- provider-contract hashes;
|
|
965
|
-
- connection lifecycle and operational status;
|
|
966
|
-
- successful `list-sites` count and matched Geny's Flowers property URL;
|
|
967
|
-
- bounded export count/date range/warnings;
|
|
968
|
-
- UI screenshot showing Test, Refresh access, and Disconnect;
|
|
969
|
-
- one request trace across main and control plane;
|
|
970
|
-
- billing quantity before and after no destructive action.
|
|
971
|
-
|
|
972
|
-
Do not include queries, page rows, tokens, credentials, or private property data in release logs.
|
|
973
|
-
|
|
974
|
-
## Acceptance criteria
|
|
975
|
-
|
|
976
|
-
The fix is complete only when all of the following are true:
|
|
977
|
-
|
|
978
|
-
1. A one-time connected read does not require the scheduler deployment.
|
|
979
|
-
2. Scheduler downtime does not break direct Search Console reads.
|
|
980
|
-
3. Control-plane downtime produces `connection_transport_unavailable` with request ID and retryability.
|
|
981
|
-
4. That downtime does not mark credentials for reconnect.
|
|
982
|
-
5. The UI distinguishes lifecycle and operational health.
|
|
983
|
-
6. `Connected` alone is never rendered as `Ready for sync`.
|
|
984
|
-
7. Search Console Test connection performs the bounded `list-sites` diagnostic.
|
|
985
|
-
8. The Geny's Flowers workflow selects an exact accessible property before querying performance.
|
|
986
|
-
9. Refresh/Reconnect and Disconnect are visible for Nango connections.
|
|
987
|
-
10. Disconnect removes upstream credential routing, local data, bindings, and billable quantity exactly once.
|
|
988
|
-
11. Reconnect preserves the logical connection and billing quantity.
|
|
989
|
-
12. Main API, control plane, and scheduler use the same provider-contract hash.
|
|
990
|
-
13. Direct and scheduled calls use the same execution, safety, and error-normalization code.
|
|
991
|
-
14. Usage receipts remain idempotent and the main Credit ledger remains authoritative.
|
|
992
|
-
15. Production bundle and backend release parity are verified automatically.
|
|
993
|
-
16. Existing healthy connections require no migration-time reauthorization.
|
|
994
|
-
17. All unit, contract, integration, staging, and production-smoke gates pass.
|
|
995
|
-
|
|
996
|
-
## Rollback
|
|
997
|
-
|
|
998
|
-
Rollback is controlled by `INTEGRATIONS_CONTROL_MODE`:
|
|
999
|
-
|
|
1000
|
-
1. return main API to `legacy` for read-only direct operations;
|
|
1001
|
-
2. keep the new additive database columns and event tables;
|
|
1002
|
-
3. stop new control-plane traffic without deleting its deployment;
|
|
1003
|
-
4. do not roll back a completed disconnect or reconnect by replaying it elsewhere;
|
|
1004
|
-
5. keep scheduler on the shared module if its tests passed—code extraction is not coupled to traffic cutover;
|
|
1005
|
-
6. retain release/hash evidence and failed request IDs for diagnosis.
|
|
1006
|
-
|
|
1007
|
-
Rollback is not allowed to restore the misleading UI. Even in legacy mode, lifecycle and operational status remain separate and transport failures remain temporary-unavailability errors.
|
|
1008
|
-
|
|
1009
|
-
## Risks and mitigations
|
|
1010
|
-
|
|
1011
|
-
| Risk | Mitigation |
|
|
1012
|
-
|---|---|
|
|
1013
|
-
| Shared Postgres access from two deployments | One migration owner, additive migrations, repository-level transactions, readiness schema check |
|
|
1014
|
-
| Duplicate provider calls during shadowing | Shadow only catalog/list/health metadata; never shadow actions, OAuth sessions, deletes, or billed exports |
|
|
1015
|
-
| New deployment adds latency | Same-region deployment, keep-alive, bounded timeouts, trace metrics |
|
|
1016
|
-
| HMAC key rotation outage | Current and previous key acceptance with short overlap and explicit key IDs |
|
|
1017
|
-
| Health probe consumes provider usage | No page-load probes; explicit test discloses one provider operation; connect probe is bounded |
|
|
1018
|
-
| Operational status becomes stale | Display timestamp and TTL; expire to unknown, never assume healthy |
|
|
1019
|
-
| Provider policy drift during migration | Canonical generated contract and release hash gate |
|
|
1020
|
-
| Deletion partially completes | Durable idempotent receipt, ordered upstream-first deletion, retryable `deleting` state |
|
|
1021
|
-
| Existing schedules bind old IDs | Reconnect preserves logical ID; disconnect cascades intentionally; migration keeps existing rows |
|
|
1022
|
-
| Production frontend remains stale | Bundle/source contract test plus live release-ID smoke |
|
|
1023
|
-
|
|
1024
|
-
## Evidence anchors
|
|
1025
|
-
|
|
1026
|
-
Current source locations supporting this specification:
|
|
1027
|
-
|
|
1028
|
-
- `src/api/nango-control.ts`: main-to-scheduler control URL, connection facade, read/action/export forwarding, error normalization.
|
|
1029
|
-
- `src/api/server.ts`: public connection, reconnect, delete, read, describe, export, action, and schedule-binding routes.
|
|
1030
|
-
- `src/mcp/http-mcp-tool-executor.ts`: root MCP-to-main connection bridge.
|
|
1031
|
-
- `public/app.jsx` and `public/app.js`: connection drawer controls and current local transport-neutral Disconnect implementation.
|
|
1032
|
-
- `tests/contract/integrations-ui.test.ts`: existing UI/API route contract checks.
|
|
1033
|
-
- `tests/unit/nango-control.test.ts`: current main-to-control request contract.
|
|
1034
|
-
- `mcp-scraper-scheduler/src/mastra/routes/nango-integrations.ts`: internal Nango catalog, connection lifecycle, read/action/describe/export routes.
|
|
1035
|
-
- `mcp-scraper-scheduler/src/mastra/db/external-connections.ts`: tenant connection rows, bindings, checkpoints, synchronized records, run receipts, and cascade relationships.
|
|
1036
|
-
- `mcp-scraper-scheduler/src/mastra/lib/nango-connection-client.ts`: Nango MCP credential boundary and per-connection client.
|
|
1037
|
-
- `mcp-scraper-scheduler/src/mastra/lib/nango-api.ts`: canonical candidate provider catalog and policy source to consolidate.
|
|
1038
|
-
- `mcp-scraper-scheduler/src/mastra/lib/connected-usage-billing.ts`: connected usage authorization and settlement boundary.
|
|
1039
|
-
|
|
1040
|
-
## Final product statement
|
|
1041
|
-
|
|
1042
|
-
A connected account and an available connection are not the same fact.
|
|
1043
|
-
|
|
1044
|
-
MCP Scraper will store OAuth lifecycle truth, measure operational truth, present both honestly, and execute direct provider work through a dedicated integration control plane. Scheduling remains an optional consumer of that capability rather than a hidden prerequisite for every connected-service call.
|