@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.
- package/CHANGELOG.md +173 -0
- package/README.md +4 -1
- package/dist/apps-shell.d.ts +13 -11
- package/dist/apps-shell.d.ts.map +1 -1
- package/dist/apps-shell.js +221 -30
- package/dist/apps-shell.js.map +1 -1
- package/dist/catalog-service.d.ts +41 -0
- package/dist/catalog-service.d.ts.map +1 -1
- package/dist/catalog-service.js +94 -5
- package/dist/catalog-service.js.map +1 -1
- package/dist/connectors/api.d.ts +5 -4
- package/dist/connectors/api.d.ts.map +1 -1
- package/dist/connectors/api.js.map +1 -1
- package/dist/connectors/remote-mcp.d.ts +5 -4
- package/dist/connectors/remote-mcp.d.ts.map +1 -1
- package/dist/connectors/remote-mcp.js.map +1 -1
- package/dist/execute.d.ts +12 -4
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +142 -20
- package/dist/execute.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/meta-tools.d.ts.map +1 -1
- package/dist/meta-tools.js +14 -4
- package/dist/meta-tools.js.map +1 -1
- package/dist/providers/mixpanel.d.ts +21 -0
- package/dist/providers/mixpanel.d.ts.map +1 -0
- package/dist/providers/mixpanel.js +183 -0
- package/dist/providers/mixpanel.js.map +1 -0
- package/dist/skills.d.ts +7 -9
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +60 -25
- package/dist/skills.js.map +1 -1
- package/dist/types.d.ts +26 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/code-mode.md +32 -20
- package/documentation/connectors.md +116 -4
- package/documentation/mcp-ui-design.md +8 -8
- package/documentation/meta-tools.md +80 -8
- package/documentation/mixpanel.md +72 -0
- package/documentation/program-ui-read-calls.md +213 -0
- package/ethos.md +10 -4
- package/package.json +5 -1
- package/src/apps-shell.ts +221 -30
- package/src/catalog-service.ts +139 -4
- package/src/connectors/api.ts +5 -3
- package/src/connectors/remote-mcp.ts +5 -3
- package/src/execute.ts +215 -21
- package/src/index.ts +1 -0
- package/src/meta-tools.ts +19 -4
- package/src/providers/mixpanel.ts +220 -0
- package/src/skills.ts +66 -24
- package/src/types.ts +27 -6
- package/src/version.ts +1 -1
- 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.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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.
|
|
48
|
-
|
|
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.
|
|
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
|
-
- **
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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.
|
|
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": {
|