@zackbart/connecta 0.12.1 → 0.13.0

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.
Files changed (58) hide show
  1. package/CHANGELOG.md +173 -0
  2. package/README.md +4 -1
  3. package/dist/apps-shell.d.ts +13 -11
  4. package/dist/apps-shell.d.ts.map +1 -1
  5. package/dist/apps-shell.js +221 -30
  6. package/dist/apps-shell.js.map +1 -1
  7. package/dist/catalog-service.d.ts +41 -0
  8. package/dist/catalog-service.d.ts.map +1 -1
  9. package/dist/catalog-service.js +94 -5
  10. package/dist/catalog-service.js.map +1 -1
  11. package/dist/connectors/api.d.ts +5 -4
  12. package/dist/connectors/api.d.ts.map +1 -1
  13. package/dist/connectors/api.js.map +1 -1
  14. package/dist/connectors/remote-mcp.d.ts +5 -4
  15. package/dist/connectors/remote-mcp.d.ts.map +1 -1
  16. package/dist/connectors/remote-mcp.js.map +1 -1
  17. package/dist/execute.d.ts +12 -4
  18. package/dist/execute.d.ts.map +1 -1
  19. package/dist/execute.js +142 -20
  20. package/dist/execute.js.map +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/meta-tools.d.ts.map +1 -1
  25. package/dist/meta-tools.js +14 -4
  26. package/dist/meta-tools.js.map +1 -1
  27. package/dist/providers/mixpanel.d.ts +21 -0
  28. package/dist/providers/mixpanel.d.ts.map +1 -0
  29. package/dist/providers/mixpanel.js +183 -0
  30. package/dist/providers/mixpanel.js.map +1 -0
  31. package/dist/skills.d.ts +7 -9
  32. package/dist/skills.d.ts.map +1 -1
  33. package/dist/skills.js +60 -25
  34. package/dist/skills.js.map +1 -1
  35. package/dist/types.d.ts +26 -6
  36. package/dist/types.d.ts.map +1 -1
  37. package/dist/version.d.ts +1 -1
  38. package/dist/version.js +1 -1
  39. package/documentation/code-mode.md +32 -20
  40. package/documentation/connectors.md +116 -4
  41. package/documentation/mcp-ui-design.md +8 -8
  42. package/documentation/meta-tools.md +80 -8
  43. package/documentation/mixpanel.md +72 -0
  44. package/documentation/program-ui-read-calls.md +213 -0
  45. package/ethos.md +10 -4
  46. package/package.json +5 -1
  47. package/src/apps-shell.ts +221 -30
  48. package/src/catalog-service.ts +139 -4
  49. package/src/connectors/api.ts +5 -3
  50. package/src/connectors/remote-mcp.ts +5 -3
  51. package/src/execute.ts +215 -21
  52. package/src/index.ts +1 -0
  53. package/src/meta-tools.ts +19 -4
  54. package/src/providers/mixpanel.ts +220 -0
  55. package/src/skills.ts +66 -24
  56. package/src/types.ts +27 -6
  57. package/src/version.ts +1 -1
  58. package/templates/node/package.json +1 -1
@@ -19,6 +19,21 @@ The consolidation removed overlapping routing choices while preserving the
19
19
  cheaper direct path for one cold call. The [guest API contract](./code-mode.md)
20
20
  is what a program is promised.
21
21
 
22
+ The route is chosen before discovery. A result that will be reduced, a call
23
+ whose arguments depend on an earlier result, or work with multiple operations
24
+ starts with one `execute_code` call and keeps discovery, calls, and reduction
25
+ inside it. Distinct operations get distinct short `connecta.search` queries in
26
+ that program. Only one unknown-address read takes the cheaper top-level
27
+ `search_tools` → `call_tool` path; a known address needs only `call_tool`.
28
+
29
+ That routing is about read-only work, because that is the only work a program
30
+ can do. Anything unannotated, write-capable, or destructive is inadmissible
31
+ inside the sandbox, so multi-step destructive work discovers at the top level
32
+ and runs each step through `call_destructive_tool` — where the host can put the
33
+ question to a human. Telling an agent never to search at the top level for
34
+ multiple calls would close the only route that work has
35
+ ([#295](https://github.com/zackbart/connecta/issues/295)).
36
+
22
37
  `execute_code` accepts optional `diagnostics: true` when a caller is measuring
23
38
  a workflow. It adds only compact request-local timing and serialized-size
24
39
  aggregates; normal calls carry no diagnostics block or response-context cost.
@@ -35,17 +50,23 @@ probing is an operator concern: the operator pages and `/health` own it.
35
50
 
36
51
  Start an unknown-address lookup with two to four distinctive action/object
37
52
  terms, not the full request, and omit `limit` so the default eight-result page
38
- stays small. Set `safety: "readOnly"` when the result is headed to `call_tool`
39
- or generated code; `safety: "approvalRequired"` finds the complementary set
40
- that must cross `call_destructive_tool`. Omitting `safety`, or setting it to
41
- `"all"`, preserves the complete configured catalog. This is only a discovery
42
- filter: it neither grants authority nor changes invocation admission.
53
+ stays small. When the integration is obvious, set `connector` to its id: a
54
+ scoped search loads that catalog alone, while an unscoped search must fan out
55
+ across every configured connector. Leave the search unscoped when the right
56
+ integration is genuinely ambiguous. Set `safety: "readOnly"` when the result is
57
+ headed to `call_tool` or generated code; `safety: "approvalRequired"` finds the
58
+ complementary set that must cross `call_destructive_tool`. Omitting `safety`,
59
+ or setting it to `"all"`, preserves the complete configured catalog. This is
60
+ only a discovery filter: it neither grants authority nor changes invocation admission.
43
61
  `includeSchemas: "compact"` adds each match's input and any declared output
44
62
  shape. Bounded plain-object schemas also expose `inputKeys`,
45
63
  `requiredInputKeys`, and `outputKeys`; a truncated shape omits its corresponding
46
64
  list rather than repeating a large partial inventory. Matches carry declared
47
- behavior annotations. When
48
- that shape is sufficient, call the returned address directly. Reserve schema
65
+ behavior annotations. Lexical rank is only one signal: select a candidate whose
66
+ required inputs are available, whose schema is complete enough for the call,
67
+ and whose safety and declared outputs fit the work. A reducer uses `outputKeys`
68
+ before inspecting the value; it does not assume a collection is named `items`
69
+ or `results`. When that shape is sufficient, call the returned address directly. Reserve schema
49
70
  expansion through `connecta.describe` for a search without schemas, an
50
71
  ambiguous compact shape, or exact
51
72
  constraints that require `format: "json"`.
@@ -60,6 +81,51 @@ types; other shapes become `unknown /* truncated */`. The match also carries
60
81
  `includeSchemas: "json"` or use the existing describe path when exact
61
82
  constraints matter.
62
83
 
84
+ ## Connector guide selection
85
+
86
+ A connector may attach a deployment-owned guide as markdown, preserving the
87
+ original `usageGuide: string` configuration, or as
88
+ `{ content, summary?, required? }`. The structured form does not register a
89
+ connector or create a shared runtime template. `content` remains the markdown
90
+ returned verbatim by `skills`; `summary` is normalized and capped at 120
91
+ characters for discovery. When it is absent, Connecta derives the same bounded
92
+ fallback used by the skills listing: the first meaningful body line, with a
93
+ heading used only when the guide has no body. `required: true` is reserved for generic
94
+ API wrappers and cross-operation conventions a complete downstream schema
95
+ cannot express.
96
+
97
+ Search and describe results keep the existing `guide: "connector:<id>"`
98
+ pointer and add `guideSummary`. A matching tool also carries
99
+ `guideRequired: true` and `guideRequiredReasons` when Connecta can prove review
100
+ is necessary:
101
+ `connector_required` for the explicit configuration above,
102
+ `approval_required` for an unannotated or write-capable tool, and
103
+ `schema_truncated` when a requested compact input or output shape was capped.
104
+ The boolean is an instruction, not a server-side gate — nothing refuses the
105
+ call, so the agent is told to fetch the guide before making it, for any reason
106
+ listed. `connector_required` and `approval_required` survive exact schema
107
+ expansion; `schema_truncated` is cleared by the describe that returns the exact
108
+ shape, and describe reports whatever reasons remain in the same two fields.
109
+ Otherwise it reads the
110
+ bounded summary: connector-specific sequencing, units, pagination, aliases,
111
+ and generic API conventions still require the guide when they affect the task,
112
+ while a complete and unambiguous one-read schema proceeds directly.
113
+ Guide lookup always uses an exact name returned by `skills({})`, search, or
114
+ describe; callers do not manufacture `connector:<id>` from an unmarked
115
+ connector.
116
+
117
+ A connector-scoped lexical miss retains that connector's guide metadata under
118
+ `queryAnalysis`. This matters for generic wrappers whose broad tool name does
119
+ not contain endpoint vocabulary: a required guide remains discoverable before
120
+ the caller falls back to an empty-query browse, rather than disappearing with
121
+ the zero-tool page.
122
+
123
+ The built-in `usage` skill is byte-identical across deployments and says to
124
+ read it at most once per task. Connector guides remain scoped to the deployment
125
+ that listed them, even when two deployments happen to use identical content.
126
+ Deployments without connector guides receive none of the conditional guide
127
+ sentences in their always-loaded tool descriptions.
128
+
63
129
  ## Result representation
64
130
 
65
131
  For object results, `structuredContent` is the canonical full-fidelity value.
@@ -122,7 +188,13 @@ results explain that no single tool covered every term and recommend splitting
122
188
  distinct intents. A true negative says that no matching capability is
123
189
  configured and recommends refining, connector-scoping, or browsing; when a
124
190
  connector catalog was unavailable, the response includes
125
- `unavailableConnectorCount` instead of making that stronger claim. Analysis
191
+ `unavailableConnectorCount` instead of making that stronger claim. A search
192
+ explicitly scoped to that unavailable connector also receives `catalogError` —
193
+ the bounded classified failure (`code`, `message`, `retryable`, and any
194
+ `retryAfterMs`) so the caller can tell a transient outage from one a deployment
195
+ operator must clear. It carries nothing else the call-path classifier knows: a
196
+ discovery read is not a call. Unscoped searches keep the count only — one
197
+ connector's failure is not another search's context. Analysis
126
198
  from a connector-filtered search includes `connectorScope` and speaks only
127
199
  about that connector; `unknownConnector` distinguishes an unconfigured ID from
128
200
  a known connector with no match. Analysis covers at most eight distinct terms
@@ -0,0 +1,72 @@
1
+ # Mixpanel prebuilt connection
2
+
3
+ Import `mixpanel()` independently from
4
+ `@zackbart/connecta/providers/mixpanel`. It wraps Mixpanel's hosted MCP server
5
+ with regional endpoint selection, OAuth by default, a provider-rate admission
6
+ budget, a task-oriented usage guide, and a vetted safety classification. It
7
+ adds no provider dependency and is not reachable from Connecta's root entry.
8
+
9
+ ```ts
10
+ import { mixpanel } from "@zackbart/connecta/providers/mixpanel";
11
+
12
+ const analytics = mixpanel("product_analytics", {
13
+ title: "Production product analytics",
14
+ purpose: "Product and growth decisions for the production app",
15
+ region: "us",
16
+ instructions: "Use the Core Product project unless the request says otherwise.",
17
+ });
18
+ ```
19
+
20
+ The `id` owns the ordinary connector namespaces; use a different id for every
21
+ Mixpanel account. `purpose` is required because an agent choosing between two
22
+ instances needs to know which account answers the question. Account
23
+ `instructions` are appended to the maintained guide and cannot change the
24
+ connector's safety classification.
25
+
26
+ `region` accepts `"us"` (the default), `"eu"`, or `"in"` and selects the
27
+ corresponding [official hosted endpoint](https://docs.mixpanel.com/docs/mcp#mcp-server-urls).
28
+ OAuth is the recommended default and keeps each connector instance's flow and
29
+ tokens in its connector-scoped storage. Mixpanel service accounts are also
30
+ supported with an explicit header override:
31
+
32
+ ```ts
33
+ mixpanel("automation_analytics", {
34
+ purpose: "Headless release-health reporting",
35
+ auth: {
36
+ type: "headers",
37
+ headers: { Authorization: `Bearer Basic ${env.MIXPANEL_SA_TOKEN}` },
38
+ },
39
+ });
40
+ ```
41
+
42
+ Keep that encoded service-account value in the runtime's secret store; it is a
43
+ password, not ordinary configuration. Mixpanel currently labels service-account
44
+ MCP authentication beta. Prefer OAuth unless the deployment is intentionally
45
+ headless.
46
+
47
+ The wrapper classifies the documented observational tools as reads and the
48
+ documented create, update, edit, merge, dismiss, duplicate, and delete tools as
49
+ writes. An unfamiliar tool added by the downstream fails closed onto
50
+ `call_destructive_tool` until a Connecta release reviews it.
51
+
52
+ That classification is **fill-in only**. It supplies the annotations Mixpanel
53
+ leaves unset and may always tighten one — but it never contradicts an explicit
54
+ downstream annotation. A tool on the read allowlist that arrives carrying
55
+ `destructiveHint: true` or `readOnlyHint: false` keeps exactly what the
56
+ downstream said and stays behind `call_destructive_tool`: the downstream is
57
+ telling you this release's allowlist is stale, and the fail-closed invariant
58
+ does not bend for a maintained connection. Maintained writes that only create
59
+ something new (`Create-Dashboard`, `Create-Cohort`, `Create-Metric`, and the
60
+ rest) leave `destructiveHint` unset; `readOnlyHint: false` already routes them
61
+ through the destructive path, and asserting destruction only inflates the
62
+ approval copy the host shows a human.
63
+
64
+ Experiments and Feature Flags — 15 of the 63 classified tools — are Mixpanel
65
+ beta surfaces. Expect their names and schemas to move faster than the rest.
66
+
67
+ The connection also declares a per-runtime call-admission budget matching
68
+ Mixpanel's documented 600 requests per hour — a best-effort approximation of
69
+ the per-user limit, not an enforcement of it. Each runtime keeps its own
70
+ counter, so N Worker isolates or Node processes serving one deployment can each
71
+ admit up to 600. Discovery traffic is outside connector call admission and
72
+ still needs restrained use.
@@ -0,0 +1,213 @@
1
+ # Bounded reads from program UI — evidence and decision
2
+
3
+ Decision note for [#287](https://github.com/zackbart/connecta/issues/287),
4
+ 2026-08-02. The implementation contract is [#289](https://github.com/zackbart/connecta/issues/289)
5
+ and the normative clauses are `V1`–`V8` in [code-mode.md](./code-mode.md).
6
+
7
+ ## Verdict
8
+
9
+ Accept explicitly bound, host-mediated **read-only** calls for refresh,
10
+ pagination, and drill-down. Keep mutations gated. Keep the one-string
11
+ `connecta.ui(html)` call display-only.
12
+
13
+ This is not an acceptance of interactive applications in general. It adds no
14
+ persistence, artifact catalog, sharing, component runtime, generated-code
15
+ library, deep link, direct network, conversation channel, or write path. One
16
+ successful program still delivers one request-local view and one ordinary text
17
+ result.
18
+
19
+ ## What display-only could not do
20
+
21
+ The current shell and the read-bound browser fixture were walked through with
22
+ the same three shapes. In the display-only arm, local JavaScript could sort,
23
+ filter, chart, expand already-delivered fields, and rerender indefinitely; every
24
+ attempt to obtain bytes not present in the original HTML stopped at the nested
25
+ frame. The read-bound arm exercised the same-origin-free nested frame, two
26
+ concurrent refreshes, fixed and view-supplied arguments, a refused fabricated
27
+ binding, a refused extra argument, and a host error.
28
+
29
+ | Workflow | Display-only attempt | Is a fresh program run adequate? | Decision |
30
+ | --- | --- | --- | --- |
31
+ | Refresh a current status or metrics view | A button can repaint only the original snapshot. Putting a timer around it changes no data. | No. It spends another model turn, reruns composition, and creates another view merely to repeat the same read. | Accept an exact named read with optional filter or cursor keys. |
32
+ | Page a cursor-backed list | The initial program can include the known next cursor, but the view cannot exchange it for the next page. Fetching every page up front defeats projection and can cross call/result budgets. | No. The model is an expensive pagination controller and has to reconstruct UI state it did not need. | Accept a binding whose declared `viewArgs` includes the cursor field. |
33
+ | Drill from a projected list into one record | Local expansion can show only fields prefetched for every row. Prefetching every detail multiplies calls and payload for records the human never opens. | Usually no. A new prompt can fetch the record, but loses the direct row selection and creates a second result instead of filling the existing view. | Accept a binding whose declared `viewArgs` includes the record identifier. |
34
+ | Sort, filter, chart, compare, or expand delivered data | Local HTML/JavaScript completes the interaction. | Yes; usually no new run is needed at all. | No call capability earned. |
35
+ | Change, delete, approve, send, or deploy | Display-only correctly cannot act. | Yes. The ordinary `call_destructive_tool` path keeps the proposed effect and host approval in the transcript. | Remain gated. A click is not approval, and this decision adds no mutation bridge. |
36
+
37
+ The accepted utility is therefore *live reads*, not “interactivity.” Local
38
+ interactivity already existed.
39
+
40
+ ## Executor comparison
41
+
42
+ Executor's inspected revision demonstrates a useful separation:
43
+
44
+ - its generated iframe disables direct `fetch`, XHR, WebSocket, EventSource,
45
+ workers, and related network primitives;
46
+ - declarative `tools.*` operations become one proxy-shaped call through a
47
+ trusted shell and an app-only action tool;
48
+ - integration roles resolve against server-owned saved bindings, and writes
49
+ can pause for shell-owned interaction handling.
50
+
51
+ That is evidence that a narrow bridge can keep untrusted markup away from raw
52
+ network and credentials. It is not evidence for Executor's React runtime,
53
+ saved artifacts, editing, previews, persistence, or deep-link fallback; those
54
+ features provide longevity and authoring ergonomics, not the refresh,
55
+ pagination, or drill-down read itself.
56
+
57
+ Connecta takes the smaller shape. The trusted shell maps names to the already
58
+ existing `call_tool`; bindings live in the completed result, not a database;
59
+ and the ordinary fail-closed read path remains the authority. No app-only tool
60
+ or eighth meta-tool is needed.
61
+
62
+ Inspected Executor sources:
63
+
64
+ - [artifact and app-only action registration](https://github.com/UsefulSoftwareCo/executor/blob/837e404acbebdf32924059d6b76f715565329307/packages/hosts/mcp/src/tool-server.ts#L1906-L2157)
65
+ - [single proxy-shaped action grammar](https://github.com/UsefulSoftwareCo/executor/blob/837e404acbebdf32924059d6b76f715565329307/packages/hosts/mcp-apps-shell/src/shell/proxy.ts#L38-L140)
66
+ - [disabled direct network primitives](https://github.com/UsefulSoftwareCo/executor/blob/837e404acbebdf32924059d6b76f715565329307/packages/hosts/mcp-apps-shell/src/shell/inner-renderer.tsx#L84-L131)
67
+
68
+ ## Contract in one pass
69
+
70
+ ```js
71
+ await connecta.ui(html, {
72
+ reads: {
73
+ refresh: {
74
+ address: "metrics.current",
75
+ fixedArgs: { service: "api" },
76
+ viewArgs: ["window", "cursor"],
77
+ },
78
+ detail: {
79
+ address: "incidents.get",
80
+ viewArgs: ["id"],
81
+ },
82
+ },
83
+ });
84
+ ```
85
+
86
+ Program markup calls `await connecta.read("detail", { id })`. It never receives
87
+ the address table. The outer shell checks the frame source, resolves `detail`,
88
+ rejects keys other than `id`, merges the arguments, and asks the host to call
89
+ the existing `call_tool` with `resultMode: "value"`.
90
+
91
+ The declaration-time catalog lookup proves the view was not born broken or
92
+ write-capable. The use-time `call_tool` lookup proves it is still read-only now.
93
+
94
+ ## Normative contract
95
+
96
+ **V1. Manifest.** The second argument is exactly
97
+ `{ reads: { name: { address, fixedArgs?, viewArgs? } } }`: 1–32 names matching
98
+ `[A-Za-z][A-Za-z0-9_-]{0,63}`, one non-empty address per name, optional fixed
99
+ arguments, and at most 32 distinct view-supplied keys. Extra fields, unsafe
100
+ control names (`__proto__`, `constructor`, `prototype`), a view key colliding
101
+ with a fixed key, and non-serializable content throw before acceptance.
102
+
103
+ **V2. Declaration admission.** Every address resolves through the request-local
104
+ catalog and passes the fail-closed `isExplicitlyReadOnly` classification before
105
+ the payload is accepted. The lookup dispatches nothing and spends no host-call
106
+ budget. `call_tool` repeats resolution and classification at use time.
107
+
108
+ **V3. Delivery.** Bindings ride beside `html` under `_meta["connecta/ui"]`,
109
+ share the existing emitted-byte aggregate, appear on success only, and never
110
+ enter `content` or `structuredContent`. The one-string payload stays exactly
111
+ `{ html }`.
112
+
113
+ **V4. Inner bridge.** A manifest alone installs `connecta.read(name, args?)`.
114
+ The outer shell accepts only its nested frame's exact `WindowProxy`, rejects an
115
+ unknown name, non-object arguments, undeclared keys, and more than eight
116
+ concurrent reads, then merges supplied keys into a null-prototype copy of fixed
117
+ arguments. The inner frame receives no address table or raw JSON-RPC.
118
+
119
+ **V5. Seven-tool boundary.** The shell calls only existing `call_tool` with
120
+ `resultMode: "value"`. That tool alone is app-visible; the other six are
121
+ explicitly model-only. No new MCP tool exists and the view cannot reach the
122
+ destructive boundary.
123
+
124
+ **V6. Ordinary admission.** A view read is a new MCP request crossing inbound
125
+ auth, request admission, current catalog and credentials, fail-closed safety,
126
+ connector admission, timeout, retry, result-size, and payload-free activity
127
+ exactly as ordinary `call_tool` does. No server-side grant or pending promise
128
+ survives the originating request.
129
+
130
+ **V7. Stale and replayed views.** A stale view retains no frozen authority:
131
+ removed tools, changed annotations, revoked credentials, lost inbound auth, and
132
+ new policy fail current admission. Replay repeats a read and may spend rate
133
+ limits, but cannot write. Cross-caller use is admitted as the current caller on
134
+ the host's originating connection; deployment remains the audience boundary.
135
+
136
+ **V8. Context, fallback, and parity.** Reads update only the human-visible view;
137
+ the return summarizes the initial snapshot and refreshed data must be labelled
138
+ as such. A host without app server tools keeps the ordinary result and initial
139
+ view while a read fails locally. The existing provider bridge carries the
140
+ manifest identically on both executors without changing `ExecuteResult`.
141
+
142
+ ## Threat and consent trace
143
+
144
+ 1. **Untrusted program declaration.** The guest supplies HTML and a strictly
145
+ shaped read manifest. The host rejects extra fields, unsafe control names,
146
+ fixed/view collisions, overlarge lists, unknown addresses, and anything not
147
+ explicitly read-only. This lookup executes nothing.
148
+ 2. **Result delivery.** On successful program completion only, HTML and
149
+ bindings ride result `_meta` under the existing aggregate byte budget. A
150
+ failed program delivers neither. The model sees only `ui: true` and the
151
+ program's initial summary.
152
+ 3. **Nested-frame request.** Only the exact payload `WindowProxy` may send the
153
+ `connecta/read` dialect to the trusted shell. A fabricated name fails before
154
+ a host call. Fabricated or prototype-shaped argument keys fail unless they
155
+ are explicitly declared; fixed keys cannot be overridden. Direct JSON-RPC
156
+ from the nested frame is ignored.
157
+ 4. **Host mediation.** The shell checks that the host advertised server-tool
158
+ calls, caps concurrent work, and emits one `tools/call` for `call_tool`.
159
+ `execute_code`, discovery, authorization, result paging, and the destructive
160
+ tool are model-only. The Apps host accepts calls only on the originating MCP
161
+ server connection.
162
+ 5. **Connecta admission.** The app call is a new authenticated request with a
163
+ fresh request scope. It crosses request admission, current catalog and
164
+ credential resolution, fail-closed read classification, connector call
165
+ admission, timeout, retry policy, result-size handling, and payload-free
166
+ activity recording. There is no UI bypass below the shell.
167
+ 6. **Result delivery.** The shell unwraps the ordinary value result and settles
168
+ only the matching inner-frame promise. Protocol errors and tool errors
169
+ reject it. It sends no result to model or conversation context.
170
+
171
+ ### Named failures
172
+
173
+ - **Fabricated address:** markup cannot submit an address; raw JSON-RPC is not
174
+ forwarded. A declaration-time invented address fails catalog resolution.
175
+ - **Fabricated binding name:** rejected by own-property lookup in the shell.
176
+ - **Fabricated argument:** an undeclared key or non-object argument is rejected;
177
+ a declared value still faces the downstream input schema and policy.
178
+ - **Stale view:** the later request reauthenticates and re-resolves the catalog.
179
+ A removed tool, newly unsafe annotation, revoked credential, or changed
180
+ admission policy fails current checks. No frozen grant exists server-side.
181
+ - **Replay:** it repeats a read and may consume rate limits, but cannot cross to
182
+ a write. The view should disable duplicate controls while its promise is
183
+ pending; the shell also bounds concurrency.
184
+ - **Cross-caller use:** the later request is admitted as the caller behind the
185
+ host's current originating connection. Connecta does not use identity to
186
+ scope tools inside one deployment; separate audiences remain separate
187
+ deployments. A copied manifest is no credential and grants nothing outside
188
+ ordinary inbound auth.
189
+ - **Destructive call:** the shell names only `call_tool`, and that handler
190
+ refuses missing, false, or contradictory read-only annotation. The
191
+ destructive meta-tool is not app-visible. No human gesture is interpreted as
192
+ write consent.
193
+
194
+ ## Invariants and parity
195
+
196
+ - **Seven tools:** unchanged; metadata makes one existing tool app-callable and
197
+ makes the other six explicitly model-only.
198
+ - **Stateless request scope:** binding state survives only in the client's
199
+ completed result and trusted shell. The server stores no grant or pending
200
+ promise.
201
+ - **Import-graph purity:** the shell remains a build-time string using browser
202
+ and Web APIs only.
203
+ - **Workers/Node parity:** the second argument crosses the existing provider
204
+ bridge, leaving `Executor` and `ExecuteResult` unchanged; the same contract
205
+ case runs on both executors and both Vitest projects.
206
+ - **Payload-free activity:** later reads use ordinary `call_tool` events, whose
207
+ schema has no arguments or results.
208
+ - **Fallback:** hosts without Apps keep the ordinary result; Apps hosts without
209
+ server-tool calls keep the initial view and fail a read locally.
210
+
211
+ The remaining gate is intentionally crisp: a mutation proposal needs real
212
+ workflow evidence plus a host-tested consent and replay story. Read utility is
213
+ not permission to smuggle that decision into this bridge.
package/ethos.md CHANGED
@@ -15,9 +15,12 @@ order, and amending it is a design decision, not a drive-by edit.
15
15
  - **A deployment is a small config-as-code file.** Changing what agents can
16
16
  reach is an edit and a redeploy. One deployment, one tenant, one audience —
17
17
  more audiences means more deployments.
18
- - **Two equal ways in.** `remoteMcp()` proxies a downstream MCP server;
19
- `api()` hand-writes a deliberate tool surface over a plain HTTP API. Both
20
- come out identical: same addresses, same catalog, same safety rules.
18
+ - **Curated when available, open when not.** Prefer an explicitly imported
19
+ prebuilt connection when Connecta maintains one: it carries the provider's
20
+ known-good endpoint, authentication defaults, tool ergonomics, and concise
21
+ usage guidance. `remoteMcp()` and `api()` remain equal, first-class
22
+ primitives for custom and unsupported integrations. Every path produces the
23
+ same `Connector`: same addresses, same catalog, same safety rules.
21
24
  - **Seven tools, an executor required.** The primary surface is a program, so
22
25
  every deployment runs an executor — a Dynamic Worker on Cloudflare, QuickJS
23
26
  behind its optional-peer subpath on Node — and one without refuses to boot
@@ -66,6 +69,8 @@ proposing one without a new argument is not.
66
69
  | Multi-tenancy / account model | refused | one deployment per tenant; deploy again instead |
67
70
  | Policy engine, approvals, pauses | refused | the host asks the human; connecta only annotates |
68
71
  | Runtime connector registration | refused | config-as-code is the security model |
72
+ | Prebuilt connections as the preferred authoring path | accepted | an a-la-carte provider constructor, imported and constructed in the deployment file, encodes maintained defaults for providers connecta actually uses — preferred *when maintained*, with no promise of one per provider; it returns exactly one ordinary `Connector` with no extra privileges — never a bundle, a group, a preset, or a registry — its tools are hand-written or proxied from a downstream MCP catalog, never generated from a schema document; its vetted annotations classify what the downstream leaves unannotated and never overrule an explicit one; `remoteMcp()` and `api()` stay first-class ([#297](https://github.com/zackbart/connecta/issues/297)) |
73
+ | Provider registry / integration marketplace | refused | prebuilt connections are imports, not listings; discovery happens in documentation, never at runtime ([#297](https://github.com/zackbart/connecta/issues/297)) |
69
74
  | Protocol sessions & server push | refused | stateless per request |
70
75
  | Resources & prompts aggregation | refused | tools only; connecta's own Apps shell is the one `resources/read` carve-out ([#266](https://github.com/zackbart/connecta/issues/266)) |
71
76
  | Elicitation passthrough | refused | no route through a stateless aggregator |
@@ -95,7 +100,8 @@ proposing one without a new argument is not.
95
100
  | Program-generated UI (`connecta.ui` + the Apps shell) | accepted | one MCP Apps view per successful run: the program supplies HTML only, delivered in result `_meta`, which hosts keep out of model context, and rendered by connecta's static shell inside the host's sandboxed frame ([design record](./documentation/mcp-ui-design.md), [#266](https://github.com/zackbart/connecta/issues/266)) |
96
101
  | Serving connecta's own UI template via `resources/read` | accepted | a narrow carve-out from the resources-aggregation refusal, not a reversal of it: one static build-time shell at one URI, an empty `resources/list`, nothing downstream ever listed or aggregated ([#266](https://github.com/zackbart/connecta/issues/266)) |
97
102
  | Downstream MCP Apps template passthrough | gated | proxying downstream `resources/read` earns its way in when a downstream connector actually ships an Apps template ([#266](https://github.com/zackbart/connecta/issues/266)) |
98
- | View-initiated tool calls from program UI | gated | the host-mediated path exists and would take the ordinary audit and consent route, but a program-authored UI driving tools needs its own argument; display-only until one arrives ([#266](https://github.com/zackbart/connecta/issues/266)) |
103
+ | View-initiated read calls from program UI | accepted | named bindings materially improve refresh, cursor pagination, and drill-down without persistence or a new tool; the trusted shell delegates only to the existing fail-closed `call_tool`, and the one-string UI remains display-only ([evidence](./documentation/program-ui-read-calls.md), [#287](https://github.com/zackbart/connecta/issues/287), [#289](https://github.com/zackbart/connecta/issues/289)) |
104
+ | View-initiated mutation calls from program UI | gated | live-read utility says nothing about write consent: a click is not approval, stale/replayed effects need a host-tested story, and the ordinary destructive path keeps the action in the transcript ([#287](https://github.com/zackbart/connecta/issues/287)) |
99
105
  | Result sampling on the catalog surface (`sample` / `dryRun`) | refused | sampling is execution and cannot ride a catalog read; most tools carry required arguments no sampler can invent, and undeclared `outputSchema` (measured 0/30 and 3/30 on real deployments) is a real gap that is not a sampleable one — a program that checks the shape before rendering already hands back the first record inside the run it was going to make anyway, at zero new surface ([#282](https://github.com/zackbart/connecta/issues/282)) |
100
106
  | Legacy embedded `UIResource` delivery | refused | superseded upstream and rendered by none of the clients connecta faces; per-request minted URIs also fight the caching the Apps spec assumes ([#266](https://github.com/zackbart/connecta/issues/266)) |
101
107
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zackbart/connecta",
3
- "version": "0.12.1",
3
+ "version": "0.13.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "One MCP to rule them all — a single MCP endpoint aggregating many downstream connectors behind a code-first surface of seven meta-tools.",
@@ -64,6 +64,10 @@
64
64
  "./auth/clerk": {
65
65
  "types": "./dist/auth/clerk.d.ts",
66
66
  "import": "./dist/auth/clerk.js"
67
+ },
68
+ "./providers/mixpanel": {
69
+ "types": "./dist/providers/mixpanel.d.ts",
70
+ "import": "./dist/providers/mixpanel.js"
67
71
  }
68
72
  },
69
73
  "scripts": {