Skip to content

Commit 3f218e4

Browse files
os-zhuangclaude
andauthored
feat(create-objectstack): default scaffold ships the three generic connector executors (#3056) (#3104)
Wire the rest/openapi/mcp connector executors + requires:['automation'] into the blank scaffold so an author can add a declarative connectors: entry and have it materialize at boot with no host code (ADR-0097). Docs + changeset included. Also fixes scaffold-e2e's Docker step to build the scaffolded app's own Dockerfile (examples/docker was removed in 6c28bac without updating the workflow). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 2ea08ee commit 3f218e4

7 files changed

Lines changed: 109 additions & 10 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
"create-objectstack": minor
3+
---
4+
5+
feat(create-objectstack): the blank scaffold ships the three generic connector executors by default
6+
7+
`npm create objectstack` now generates an `objectstack.config.ts` that wires the
8+
`rest`, `openapi`, and `mcp` connector executor plugins (ADR-0022/0023/0024 +
9+
ADR-0097) into `plugins:`, alongside `requires: ['automation']`. This closes the
10+
last authoring gap in the ADR-0097 promise that integrations are expressible
11+
**and executable** as pure metadata: an author (human or AI) can now add a
12+
declarative `connectors:` entry naming `provider: 'rest' | 'openapi' | 'mcp'`
13+
and have it materialize into a live, dispatchable connector at boot — with no
14+
host-code edit.
15+
16+
- `plugins:``new ConnectorRestPlugin()`, `new ConnectorOpenApiPlugin()`,
17+
`new ConnectorMcpPlugin()` (zero-arg = contribute the provider factory only).
18+
- `requires: ['automation']` — the automation service performs the
19+
materialization and owns the registry the executors register into. It is also
20+
a hard dependency of the connector plugins, so a scaffold that lists them in
21+
`plugins:` without it fails boot; automation ships transitively via
22+
`@objectstack/cli`.
23+
- deps — `@objectstack/connector-rest`, `@objectstack/connector-openapi`,
24+
`@objectstack/connector-mcp`.
25+
- Security (#3055): declarative `mcp` stdio transports stay denied by default —
26+
opt in per host with `new ConnectorMcpPlugin({ declarativeStdio: ['node'] })`.
27+
28+
Brand connectors (Slack, …) remain marketplace/opt-in.

.github/workflows/scaffold-e2e.yml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ on:
1919
pull_request:
2020
paths:
2121
- 'packages/create-objectstack/**'
22-
- 'examples/docker/**'
2322
- 'docker/**'
2423
- '.github/workflows/scaffold-e2e.yml'
2524
schedule:
@@ -116,8 +115,9 @@ jobs:
116115
kill "$SERVER_PID"
117116
118117
- name: Build official runtime image from this checkout
119-
# examples/docker builds FROM ghcr.io/objectstack-ai/objectstack.
120-
# Build that base HERE from docker/Dockerfile instead of pulling, so
118+
# The scaffolded app's Dockerfile builds FROM
119+
# ghcr.io/objectstack-ai/objectstack. Build that base HERE from
120+
# docker/Dockerfile instead of pulling, so
121121
# (a) PRs exercise the official runtime Dockerfile itself, and
122122
# (b) the e2e stays hermetic — no dependency on a prior release
123123
# having published the tag (chicken-and-egg on the very first one).
@@ -126,12 +126,13 @@ jobs:
126126
--build-arg OS_CLI_VERSION=latest \
127127
"$GITHUB_WORKSPACE/docker"
128128
129-
- name: Docker build and run (examples/docker)
129+
- name: Docker build and run (scaffolded Dockerfile)
130+
# The blank template ships its own Dockerfile / docker-compose.yml /
131+
# .dockerignore, so build the generated project as-is — this exercises
132+
# the exact Docker path a real `npm create objectstack` user ships (the
133+
# standalone examples/docker copy was removed in 6c28bac).
130134
run: |
131135
cd "$RUNNER_TEMP/e2e-app"
132-
cp "$GITHUB_WORKSPACE"/examples/docker/Dockerfile \
133-
"$GITHUB_WORKSPACE"/examples/docker/docker-compose.yml \
134-
"$GITHUB_WORKSPACE"/examples/docker/.dockerignore .
135136
docker build --pull=false -t e2e-app .
136137
docker run -d --name e2e -p 18080:8080 \
137138
-e OS_SECRET_KEY="$(openssl rand -hex 32)" \

content/docs/automation/flows.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ Each node performs a specific action in the flow.
117117
| `map` | Sequential multi-instance — run a per-item subflow for each element, one at a time (each may pause); batch approval (ADR-0039) |
118118
| `connector_action` | Execute an external connector action |
119119

120-
`connector_action` dispatches against the runtime **connector registry**, which connector plugins populate. The three **generic executors**`rest`, `openapi`, and `mcp` — double as provider factories: with them in your app's `plugins:`, a declarative `connectors:` entry that names a `provider` is materialized into a live, dispatchable connector at boot with no plugin code (ADR-0097; the showcase example wires all three). Brand connectors (Slack, …) are installed separately. One security note: a declarative `mcp` instance using a **stdio** transport spawns a local process from metadata and is therefore denied by default — the host opts in with `new ConnectorMcpPlugin({ declarativeStdio: ['<trusted-command>'] })`; `http` transports need no opt-in.
120+
`connector_action` dispatches against the runtime **connector registry**, which connector plugins populate. The three **generic executors**`rest`, `openapi`, and `mcp` — double as provider factories: with them in your app's `plugins:`, a declarative `connectors:` entry that names a `provider` is materialized into a live, dispatchable connector at boot with no plugin code (ADR-0097). Scaffolded projects (`npm create objectstack`) ship all three executors by default — paired with the `automation` capability that performs the materialization — and the showcase wires them too. Brand connectors (Slack, …) are installed separately. One security note: a declarative `mcp` instance using a **stdio** transport spawns a local process from metadata and is therefore denied by default — the host opts in with `new ConnectorMcpPlugin({ declarativeStdio: ['<trusted-command>'] })`; `http` transports need no opt-in.
121121

122122
### Node Structure
123123

content/docs/getting-started/your-first-project.mdx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ filename-suffix magic — metadata exists in the app only if it is imported here
101101

102102
```typescript title="objectstack.config.ts"
103103
import { defineStack } from '@objectstack/spec';
104+
import { ConnectorRestPlugin } from '@objectstack/connector-rest';
105+
import { ConnectorOpenApiPlugin } from '@objectstack/connector-openapi';
106+
import { ConnectorMcpPlugin } from '@objectstack/connector-mcp';
104107
import * as objects from './src/objects/index.js';
105108

106109
export default defineStack({
@@ -111,12 +114,25 @@ export default defineStack({
111114
type: 'app',
112115
name: 'My App',
113116
},
117+
// `automation` runs flows and, per ADR-0097, materializes declarative
118+
// `connectors:` entries at boot. The three generic executors below register
119+
// their `rest` / `openapi` / `mcp` provider factories with it.
120+
requires: ['automation'],
121+
plugins: [
122+
new ConnectorRestPlugin(),
123+
new ConnectorOpenApiPlugin(),
124+
new ConnectorMcpPlugin(),
125+
],
114126
objects: Object.values(objects),
115127
});
116128
```
117129

118130
The REST API needs no configuration — it is on by default for every object
119-
with `apiEnabled`.
131+
with `apiEnabled`. The `plugins:` array ships the three **generic connector
132+
executors**, so you can integrate an external REST / OpenAPI / MCP service as
133+
pure metadata: add a `connectors:` entry naming a `provider` and the
134+
`automation` capability materializes it into a live connector at boot — see
135+
[Automation → Flows](/docs/automation/flows).
120136

121137
**`src/objects/note.object.ts`** is a complete data model — schema, validation,
122138
API surface, and searchability in one typed definition:
@@ -151,6 +167,7 @@ framework surface you install:
151167
| `@objectstack/runtime` | The kernel that interprets metadata at runtime. |
152168
| `@objectstack/driver-memory` | In-memory database driver for development. Swap for SQLite / Postgres / MongoDB in production — [no code changes](/docs/data-modeling/drivers). |
153169
| `@objectstack/plugin-hono-server` | The HTTP server that mounts the generated REST API. |
170+
| `@objectstack/connector-rest` · `-openapi` · `-mcp` | The three generic connector executors — register the `rest` / `openapi` / `mcp` provider factories so declarative `connectors:` entries materialize ([ADR-0097](/docs/automation/flows)). |
154171
| `@objectstack/cli` *(dev)* | The `os` / `objectstack` CLI: `dev`, `validate`, `build`, `start`. |
155172

156173
## 3. Run it

packages/create-objectstack/src/templates/blank/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,32 @@ curl -b cookies.txt "http://localhost:3000/api/v1/data/<your_object>"
2626
- `objectstack.config.ts` — environment manifest (objects, API, plugins)
2727
- `src/objects/` — object definitions (one file per object)
2828

29+
## Connectors (default providers)
30+
31+
`objectstack.config.ts` wires the three **generic connector executors**, so you
32+
can call an external system from a flow as pure metadata — no host code:
33+
34+
| Provider | Package | Use for |
35+
|:---|:---|:---|
36+
| `rest` | `@objectstack/connector-rest` | Any JSON/HTTP REST API |
37+
| `openapi` | `@objectstack/connector-openapi` | An API described by an OpenAPI document |
38+
| `mcp` | `@objectstack/connector-mcp` | A Model Context Protocol server |
39+
40+
Add a `connectors:` entry that names one of these `provider`s and the
41+
`automation` capability materializes it into a live, dispatchable connector at
42+
boot (ADR-0097); a flow's `connector_action` node then calls it. To add a brand
43+
connector (e.g. Slack), install its package and add `new ConnectorSlackPlugin()`
44+
to `plugins:`; to drop a provider, remove its plugin.
45+
46+
> **Security — declarative MCP over stdio.** An `mcp` connector whose transport
47+
> spawns a local process (`stdio`) is denied by default, because the command
48+
> comes from metadata. Opt in per host with
49+
> `new ConnectorMcpPlugin({ declarativeStdio: ['node'] })`; `http` transports
50+
> need no opt-in.
51+
52+
See [Automation → Flows](https://docs.objectstack.ai/docs/automation/flows) for
53+
the full connector and `connector_action` guide.
54+
2955
## Verify your changes
3056

3157
After editing any metadata, run:

packages/create-objectstack/src/templates/blank/objectstack.config.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import { defineStack } from '@objectstack/spec';
2+
import { ConnectorRestPlugin } from '@objectstack/connector-rest';
3+
import { ConnectorOpenApiPlugin } from '@objectstack/connector-openapi';
4+
import { ConnectorMcpPlugin } from '@objectstack/connector-mcp';
25
import * as objects from './src/objects/index.js';
36

47
export default defineStack({
@@ -15,5 +18,26 @@ export default defineStack({
1518
// scripts/sync-template-versions.mjs.
1619
engines: { protocol: '^15' },
1720
},
21+
22+
// `automation` backs flow execution and, per ADR-0097, materializes any
23+
// declarative `connectors:` entry into a live, dispatchable connector at boot.
24+
// The connector executors below register their provider factories with it —
25+
// without `automation` loaded they have nowhere to register and boot fails, so
26+
// keep this capability whenever `plugins:` lists a connector.
27+
requires: ['automation'],
28+
29+
// Generic connector executors (ADR-0022/0023/0024 + ADR-0097), default-present
30+
// so you can add a `connectors:` entry naming `provider: 'rest' | 'openapi' |
31+
// 'mcp'` and have it materialize with zero host code. Zero-arg = contribute the
32+
// provider factory only. Brand connectors (Slack, …) stay marketplace/opt-in.
33+
// Security (#3055): a declarative `mcp` stdio transport spawns a local process
34+
// from metadata and is denied by default — opt in per host with
35+
// `new ConnectorMcpPlugin({ declarativeStdio: ['<trusted-command>'] })`.
36+
plugins: [
37+
new ConnectorRestPlugin(),
38+
new ConnectorOpenApiPlugin(),
39+
new ConnectorMcpPlugin(),
40+
],
41+
1842
objects: Object.values(objects),
1943
});

packages/create-objectstack/src/templates/blank/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
"@objectstack/spec": "^15.0.0",
1515
"@objectstack/runtime": "^15.0.0",
1616
"@objectstack/driver-memory": "^15.0.0",
17-
"@objectstack/plugin-hono-server": "^15.0.0"
17+
"@objectstack/plugin-hono-server": "^15.0.0",
18+
"@objectstack/connector-rest": "^15.0.0",
19+
"@objectstack/connector-openapi": "^15.0.0",
20+
"@objectstack/connector-mcp": "^15.0.0"
1821
},
1922
"devDependencies": {
2023
"@objectstack/cli": "^15.0.0",

0 commit comments

Comments
 (0)