eve 0.16.0 → 0.16.2

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 (68) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/src/channel/compiled-channel.d.ts +2 -0
  3. package/dist/src/channel/cors.d.ts +43 -0
  4. package/dist/src/channel/cors.js +1 -0
  5. package/dist/src/cli/dev/tui/agent-info-probe.d.ts +16 -0
  6. package/dist/src/cli/dev/tui/agent-info-probe.js +1 -0
  7. package/dist/src/cli/dev/tui/mcp-connection-status.d.ts +19 -0
  8. package/dist/src/cli/dev/tui/mcp-connection-status.js +1 -0
  9. package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
  10. package/dist/src/cli/dev/tui/remote-connection-probe.js +1 -1
  11. package/dist/src/cli/dev/tui/runner.d.ts +4 -0
  12. package/dist/src/cli/dev/tui/runner.js +1 -1
  13. package/dist/src/cli/dev/tui/setup-commands.d.ts +1 -0
  14. package/dist/src/cli/dev/tui/setup-commands.js +1 -1
  15. package/dist/src/cli/dev/tui/setup-panel.js +1 -1
  16. package/dist/src/cli/dev/tui/tui.js +1 -1
  17. package/dist/src/compiler/manifest.d.ts +8 -2
  18. package/dist/src/compiler/manifest.js +1 -1
  19. package/dist/src/compiler/normalize-channel.js +1 -1
  20. package/dist/src/context/providers/connection-key.d.ts +9 -0
  21. package/dist/src/context/providers/connection-key.js +1 -0
  22. package/dist/src/context/providers/connection.d.ts +1 -9
  23. package/dist/src/context/providers/connection.js +1 -1
  24. package/dist/src/harness/input-requests.d.ts +1 -0
  25. package/dist/src/harness/input-requests.js +1 -1
  26. package/dist/src/harness/tool-loop.js +1 -1
  27. package/dist/src/internal/application/package.js +1 -1
  28. package/dist/src/internal/nitro/host/channel-routes.d.ts +2 -0
  29. package/dist/src/internal/nitro/host/channel-routes.js +5 -3
  30. package/dist/src/internal/nitro/host/start-development-server.js +2 -2
  31. package/dist/src/internal/workflow/runtime.js +1 -1
  32. package/dist/src/packages/eve-catalog/src/index.js +1 -1
  33. package/dist/src/public/channels/eve.d.ts +31 -0
  34. package/dist/src/public/channels/eve.js +1 -1
  35. package/dist/src/public/channels/github/githubChannel.js +1 -1
  36. package/dist/src/public/channels/github/inbound-types.d.ts +189 -0
  37. package/dist/src/public/channels/github/inbound-types.js +1 -0
  38. package/dist/src/public/channels/github/inbound.d.ts +2 -190
  39. package/dist/src/public/channels/github/inbound.js +2 -2
  40. package/dist/src/public/channels/index.d.ts +1 -1
  41. package/dist/src/public/definitions/defineChannel.d.ts +10 -0
  42. package/dist/src/public/definitions/defineChannel.js +1 -1
  43. package/dist/src/runtime/connections/scoped-authorization.d.ts +9 -0
  44. package/dist/src/runtime/connections/scoped-authorization.js +1 -1
  45. package/dist/src/runtime/connections/types.d.ts +4 -4
  46. package/dist/src/runtime/framework-channels/index.js +1 -1
  47. package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -1
  48. package/dist/src/runtime/framework-tools/index.d.ts +1 -1
  49. package/dist/src/runtime/framework-tools/index.js +1 -1
  50. package/dist/src/runtime/framework-tools/skill.js +1 -1
  51. package/dist/src/runtime/resolve-channel.js +1 -1
  52. package/dist/src/runtime/types.d.ts +2 -0
  53. package/dist/src/setup/boxes/select-connections.js +1 -1
  54. package/dist/src/setup/cli/option-row.d.ts +2 -0
  55. package/dist/src/setup/cli/option-row.js +1 -1
  56. package/dist/src/setup/flows/connections.d.ts +1 -0
  57. package/dist/src/setup/flows/connections.js +2 -2
  58. package/dist/src/setup/scaffold/create/project.js +1 -1
  59. package/docs/channels/custom.mdx +19 -0
  60. package/docs/channels/eve.mdx +23 -0
  61. package/docs/concepts/sessions-runs-and-streaming.md +2 -0
  62. package/docs/connections/mcp.mdx +161 -19
  63. package/docs/connections/openapi.mdx +133 -36
  64. package/docs/connections/overview.mdx +4 -4
  65. package/docs/patterns/multi-tenant-approvals.md +4 -0
  66. package/docs/patterns/multi-tenant-auth.md +156 -37
  67. package/docs/tools/human-in-the-loop.md +3 -1
  68. package/package.json +1 -1
@@ -45,6 +45,29 @@ curl -N https://<deployment>/eve/v1/session/ses_01h.../stream
45
45
 
46
46
  See [Sessions, runs & streaming](../concepts/sessions-runs-and-streaming) for the full request and stream flow, including the complete event set.
47
47
 
48
+ ## CORS
49
+
50
+ The eve channel leaves CORS untouched by default. Pass `cors: true` to enable
51
+ permissive browser CORS with preflight handling, or pass an options object to
52
+ narrow origins, methods, and headers. Route auth still runs on the actual
53
+ session requests.
54
+
55
+ Enable or narrow CORS only when browser clients call the channel directly:
56
+
57
+ ```ts title="agent/channels/eve.ts"
58
+ import { eveChannel } from "eve/channels/eve";
59
+ import { localDev, vercelOidc } from "eve/channels/auth";
60
+
61
+ export default eveChannel({
62
+ auth: [vercelOidc(), localDev()],
63
+ cors: {
64
+ origin: "https://app.example.com",
65
+ methods: ["GET", "POST"],
66
+ allowedHeaders: ["authorization", "content-type"],
67
+ },
68
+ });
69
+ ```
70
+
48
71
  ## Authentication
49
72
 
50
73
  The `auth` option decides who can call these routes. The built-in helpers cover development and trusted infrastructure:
@@ -84,6 +84,8 @@ curl -X POST http://127.0.0.1:3000/eve/v1/session/<sessionId> \
84
84
 
85
85
  The follow-up reuses the same durable session: same history, same state.
86
86
 
87
+ If the session is waiting on a human-in-the-loop approval, a matching text reply such as `approve` or `deny` answers the approval. Other follow-up text is held until the approval is answered, so an unrelated message does not implicitly deny the pending tool call.
88
+
87
89
  For deterministic ordering, send one follow-up at a time and wait for the next `session.waiting` event before sending another message to the same session. See [message delivery and queueing](./execution-model-and-durability#message-delivery-and-queueing) for the current runtime contract.
88
90
 
89
91
  ## Reconnect and rewind
@@ -1,21 +1,71 @@
1
1
  ---
2
2
  title: "MCP Connections"
3
- description: "Connect an eve agent to a remote MCP server and control which tools the model can discover."
3
+ description: "Connect an eve agent to a remote MCP server, authorize it with Vercel Connect or static credentials, and control which tools the model can discover."
4
4
  ---
5
5
 
6
- MCP connections point eve at an MCP server you do not author. The server publishes its tools and schemas, and eve exposes matched tools to the model through `connection_search`.
6
+ MCP connections point eve at a remote MCP server you do not author. The server publishes its tools and schemas, and eve exposes matching tools to the model through `connection_search`.
7
7
 
8
- Use MCP when the service already has an MCP server, when the server needs to own tool schemas dynamically, or when one connection should expose a family of related remote tools.
8
+ Use MCP when the service already has an MCP server, when the server owns tool schemas dynamically, or when one connection should expose a family of related remote tools. Use an [OpenAPI connection](./openapi) instead when the service publishes an HTTP API contract and you want eve to generate one tool per operation.
9
9
 
10
10
  ## Define an MCP connection
11
11
 
12
- `defineMcpClientConnection` needs a `url` and a `description`:
12
+ Create one file under `agent/connections/`. The filename becomes the runtime connection name, so `agent/connections/linear.ts` registers as `linear`, and discovered tools are called as `linear__<tool>`.
13
13
 
14
14
  ```ts title="agent/connections/linear.ts"
15
+ import { connect } from "@vercel/connect/eve";
15
16
  import { defineMcpClientConnection } from "eve/connections";
16
17
 
17
18
  export default defineMcpClientConnection({
18
- url: "https://mcp.linear.app/sse",
19
+ url: "https://mcp.linear.app/mcp",
20
+ description: "Linear workspace: issues, projects, cycles, and comments.",
21
+ auth: connect("mcp.linear.app/linear"),
22
+ });
23
+ ```
24
+
25
+ The `url` must speak Streamable HTTP or SSE. Write the `description` for the model, not for yourself: it is the main signal `connection_search` uses when deciding which connection to query.
26
+
27
+ ## Use Vercel Connect for OAuth
28
+
29
+ For OAuth-backed MCP servers, prefer [Vercel Connect](https://vercel.com/docs/connect). Connect owns the browser consent flow, encrypted token storage, refresh, and project access. The `connect()` helper from `@vercel/connect/eve` plugs that lifecycle into eve's connection auth, so credentials never land in model context or conversation history.
30
+
31
+ From the Vercel project or agent app that will run the connection:
32
+
33
+ ```bash
34
+ npm install @vercel/connect
35
+ vercel link
36
+ vercel connect create mcp.linear.app --name linear
37
+ vercel connect attach <connector-uid> --yes
38
+ vercel env pull
39
+ ```
40
+
41
+ Use the connector UID returned by the CLI in `connect("<connector-uid>")`. For Linear, the MCP runtime endpoint is `https://mcp.linear.app/mcp`, while the managed Connect service identifier is `mcp.linear.app`; those are related but not interchangeable.
42
+
43
+ By default, `connect("...")` is user-scoped. The first tool call for a new user emits an `authorization.required` event with a URL to visit, parks the turn, and resumes after the callback completes. The active eve session must already have a user principal from route auth or from a platform channel. If there is no authenticated user, the connection fails with `reason: "principal_required"`.
44
+
45
+ If the MCP server should act as the agent itself instead of the signed-in user, make the connector app-scoped:
46
+
47
+ ```ts title="agent/connections/linear.ts"
48
+ import { connect } from "@vercel/connect/eve";
49
+ import { defineMcpClientConnection } from "eve/connections";
50
+
51
+ export default defineMcpClientConnection({
52
+ url: "https://mcp.linear.app/mcp",
53
+ description: "Linear workspace: issues, projects, cycles, and comments.",
54
+ auth: connect({ connector: "mcp.linear.app/linear", principalType: "app" }),
55
+ });
56
+ ```
57
+
58
+ App-scoped Connect auth is non-interactive. eve asks Connect for one shared app token; if the connector is missing or cannot issue an app token, the tool call fails terminally so an operator can fix setup.
59
+
60
+ ## Static tokens and headers
61
+
62
+ Use `auth.getToken` when you already have a bearer token, API key, service account token, or out-of-band OAuth flow. eve sends the returned token as `Authorization: Bearer <token>`.
63
+
64
+ ```ts title="agent/connections/linear.ts"
65
+ import { defineMcpClientConnection } from "eve/connections";
66
+
67
+ export default defineMcpClientConnection({
68
+ url: "https://mcp.linear.app/mcp",
19
69
  description: "Linear workspace: issues, projects, cycles, and comments.",
20
70
  auth: {
21
71
  getToken: async () => ({ token: process.env.LINEAR_API_TOKEN! }),
@@ -23,39 +73,131 @@ export default defineMcpClientConnection({
23
73
  });
24
74
  ```
25
75
 
26
- The `url` must speak Streamable HTTP or SSE. Write the `description` for the model, not for yourself. It shows up in `connection_search`, and the model uses it to decide which connection to query.
76
+ Use `headers` for non-Bearer auth schemes or extra server configuration:
77
+
78
+ ```ts title="agent/connections/private-docs.ts"
79
+ import { defineMcpClientConnection } from "eve/connections";
80
+
81
+ export default defineMcpClientConnection({
82
+ url: "https://docs.example.com/mcp",
83
+ description: "Internal docs: search pages, owners, and recent changes.",
84
+ headers: {
85
+ "X-Api-Key": process.env.DOCS_API_KEY!,
86
+ },
87
+ });
88
+ ```
27
89
 
28
- The file path provides the connection name. `agent/connections/linear.ts` registers as `linear`, and remote tools appear as `linear__<tool>` after discovery.
90
+ `auth` and `headers` can also be functions that receive the active session context. Use that when credentials, tenants, or routing depend on the caller.
91
+
92
+ ## No auth
93
+
94
+ Omit `auth` and `headers` only for intentionally public or local-only servers:
95
+
96
+ ```ts title="agent/connections/local.ts"
97
+ import { defineMcpClientConnection } from "eve/connections";
98
+
99
+ export default defineMcpClientConnection({
100
+ url: "http://localhost:3001/mcp",
101
+ description: "Local dev MCP server.",
102
+ });
103
+ ```
104
+
105
+ Do not use a no-auth connection for sensitive third-party services unless another layer protects the endpoint.
29
106
 
30
107
  ## Tool filters
31
108
 
32
- To narrow which remote tools the model sees, set exactly one of `tools.allow` or `tools.block`. Filtered-out tools do not appear in `connection_search`:
109
+ MCP servers can expose broad read and write surfaces. Narrow what the model can discover with exactly one of `tools.allow` or `tools.block`:
33
110
 
34
111
  ```ts title="agent/connections/linear.ts"
112
+ import { connect } from "@vercel/connect/eve";
35
113
  import { defineMcpClientConnection } from "eve/connections";
36
114
 
37
115
  export default defineMcpClientConnection({
38
- url: "https://mcp.linear.app/sse",
39
- description: "Linear: read-only.",
40
- auth: { getToken: async () => ({ token: process.env.LINEAR_API_TOKEN! }) },
116
+ url: "https://mcp.linear.app/mcp",
117
+ description: "Linear: read issue and project data.",
118
+ auth: connect("mcp.linear.app/linear"),
41
119
  tools: { allow: ["search_issues", "get_issue"] },
42
120
  });
43
121
  ```
44
122
 
45
- Use `allow` for the smallest safe surface, especially on MCP servers that expose write tools alongside read tools. Use `block` only when the server has a broad stable surface and you need to hide a few tools.
123
+ Prefer `allow` for the smallest safe surface, especially when the server exposes write tools. Use `block` when the server has a broad stable surface and only a few tools should be hidden.
124
+
125
+ ## Approval gates
126
+
127
+ For MCP tools that can create, modify, delete, message, transmit, purchase, or access sensitive data, add a human approval gate:
128
+
129
+ ```ts title="agent/connections/linear.ts"
130
+ import { connect } from "@vercel/connect/eve";
131
+ import { defineMcpClientConnection } from "eve/connections";
132
+ import { once } from "eve/tools/approval";
133
+
134
+ export default defineMcpClientConnection({
135
+ url: "https://mcp.linear.app/mcp",
136
+ description: "Linear workspace.",
137
+ auth: connect("mcp.linear.app/linear"),
138
+ approval: once(),
139
+ });
140
+ ```
141
+
142
+ `once()` asks the first time in a session, `always()` asks on every tool call, and `never()` allows calls without approval. Authorization and approval work together: eve records approval before an OAuth park, then resumes without asking again for the same approved call.
143
+
144
+ The `once()`, `always()`, and `never()` helpers from `eve/tools/approval` gate every tool the connection serves the same way. See [Connections](/docs/connections#per-connection-approval) for those, and [Human-in-the-loop](/docs/human-in-the-loop) for the full pause-and-resume contract.
145
+
146
+ ### Gate specific tools by name or input
147
+
148
+ A remote MCP server usually mixes read tools with destructive or publishing ones, so a blanket `always()` would prompt on every harmless call. Pass a custom policy instead — the same [`Approval`](/docs/human-in-the-loop#approvals) shape authored tools use — to gate only the calls that matter. The policy receives `{ session, toolName, toolInput, approvedTools }` and returns an approval status, synchronously or as a promise.
149
+
150
+ This connection always gates deletes, gates a publish only when the call actually schedules a post, and lets everything else through:
151
+
152
+ ```ts title="agent/connections/social.ts"
153
+ import { defineMcpClientConnection } from "eve/connections";
154
+
155
+ // Bare tool names whose effects are irreversible — always gate these.
156
+ const DELETE_TOOLS = ["delete_draft", "delete_thread"];
157
+ // Tools that can publish — gate only when the call schedules a post.
158
+ const PUBLISH_TOOLS = ["create_draft", "edit_draft"];
159
+
160
+ // Read `requestBody.publish_at` without trusting the input's shape.
161
+ const publishesNow = (input: unknown): boolean => {
162
+ const body = (input as { requestBody?: { publish_at?: unknown } })?.requestBody;
163
+ return typeof body?.publish_at === "string" && body.publish_at.length > 0;
164
+ };
165
+
166
+ export default defineMcpClientConnection({
167
+ url: "https://mcp.example.com/mcp",
168
+ description: "Social publishing: draft, schedule, and manage posts.",
169
+ auth: { getToken: async () => ({ token: process.env.SOCIAL_API_KEY! }) },
170
+ approval: ({ toolName, toolInput }) => {
171
+ if (DELETE_TOOLS.some((t) => toolName.includes(t))) return "user-approval";
172
+ if (PUBLISH_TOOLS.some((t) => toolName.includes(t))) {
173
+ return publishesNow(toolInput) ? "user-approval" : "not-applicable";
174
+ }
175
+ return "not-applicable";
176
+ },
177
+ });
178
+ ```
179
+
180
+ Two details are specific to connection tools:
181
+
182
+ - **`toolName` arrives qualified**, not as the bare remote name. An MCP tool surfaces to the policy as `<connection>__<tool>` (e.g. `social__delete_draft`), so match the bare tool name with `.includes()` or `.endsWith()` rather than `===`.
183
+ - **`toolInput` is the raw input the model produced**, typed as `Record<string, unknown> | undefined`. With an authored tool you define the `inputSchema`, so its approval policy gets input typed and checked against your schema; a connection tool's schema is published by the remote MCP server, not you, so the shape is one you neither own nor can rely on. It is also `undefined` whenever the model's input isn't an object. Read nested fields defensively — as `publishesNow` does — instead of trusting the shape.
46
184
 
47
- ## Auth, headers, and approval
185
+ Return `"user-approval"` (or `true`) to pause for a person and `"not-applicable"` (or `false`) to run without a prompt; return `"approved"` or `"denied"` to decide automatically without involving anyone.
48
186
 
49
- MCP connections support the shared connection options:
187
+ [Human-in-the-loop](/docs/human-in-the-loop#approvals) covers the full set of statuses, how `approvedTools` and `session.auth` factor in, and how a gated call pauses and resumes durably.
50
188
 
51
- - `auth` for static tokens, Vercel Connect, self-hosted interactive OAuth, or a per-caller provider resolver.
52
- - `headers` for static or per-caller API-key schemes and extra server configuration.
53
- - `approval` for human-in-the-loop gates before connection tools run.
189
+ ## Troubleshooting
54
190
 
55
- See [Connections](/docs/connections) for the shared auth, headers, and approval shapes.
191
+ | Symptom | Check |
192
+ | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
193
+ | `principal_required` | A user-scoped `connect("...")` ran without an authenticated user. Return `principalType: "user"` from route auth, or use app-scoped auth. |
194
+ | The model does not find the remote tool | Improve the connection `description`, then check `tools.allow` / `tools.block`. |
195
+ | OAuth works locally but fails after deploy | Attach the Connect connector to the deployed Vercel project and verify the UID in `connect("...")`. |
196
+ | The server rejects requests | Confirm the MCP URL, transport support, auth scheme, and any required headers. |
56
197
 
57
198
  ## What to read next
58
199
 
200
+ - [Connections](../connections): shared auth, headers, approval, and per-caller patterns.
59
201
  - [OpenAPI connections](./openapi): generate tools from OpenAPI operations.
60
- - [Auth & route protection](../guides/auth-and-route-protection): the full interactive-OAuth flow with Vercel Connect.
202
+ - [Auth & route protection](../guides/auth-and-route-protection): route auth and the full interactive OAuth lifecycle.
61
203
  - [Security model](../concepts/security-model): how connection credentials stay out of the model's reach.
@@ -1,13 +1,15 @@
1
1
  ---
2
2
  title: "OpenAPI Connections"
3
- description: "Turn an OpenAPI 3.x document into eve connection tools, one tool per operation."
3
+ description: "Turn an OpenAPI 3.x or Swagger 2.0 document into eve connection tools, authorize calls, and control which operations the model can discover."
4
4
  ---
5
5
 
6
- OpenAPI connections turn any OpenAPI 3.x document into connection tools, one per operation. Use OpenAPI when a service already publishes an HTTP API contract and you want eve to derive the model-facing tools from that contract.
6
+ OpenAPI connections turn an OpenAPI 3.x or Swagger 2.0 document into connection tools, one per operation. Use OpenAPI when a service publishes an HTTP API contract and you want eve to derive model-facing tools from that contract.
7
+
8
+ Use an [MCP connection](./mcp) instead when the service already exposes an MCP server, when the server should own tool schemas dynamically, or when the remote service has richer MCP semantics than its raw HTTP API.
7
9
 
8
10
  ## Define an OpenAPI connection
9
11
 
10
- `defineOpenAPIConnection` takes an HTTPS URL that eve fetches at runtime, or an inline parsed object:
12
+ Create one file under `agent/connections/`. The filename becomes the runtime connection name, so `agent/connections/petstore.ts` registers as `petstore`, and generated operation tools are called as `petstore__<operation>`.
11
13
 
12
14
  ```ts title="agent/connections/petstore.ts"
13
15
  import { defineOpenAPIConnection } from "eve/connections";
@@ -19,24 +21,120 @@ export default defineOpenAPIConnection({
19
21
  });
20
22
  ```
21
23
 
22
- Each operation becomes `<connection>__<operationId>` (e.g. `petstore__getInventory`). When an operation has no `operationId`, eve derives a deterministic `<method>_<sanitized-path>` name instead.
24
+ Each operation becomes `<connection>__<operationId>`, for example `petstore__getInventory`. When an operation has no `operationId`, eve derives a deterministic `<method>_<sanitized-path>` name instead.
25
+
26
+ `spec` can be an HTTPS URL that eve fetches at runtime, or an inline parsed OpenAPI object. Prefer a URL when the provider owns the contract and updates it; prefer an inline object for private APIs, generated specs you pin in source control, or small hand-authored contracts.
27
+
28
+ ## Base URL and servers
29
+
30
+ eve resolves operation paths against `baseUrl` when you provide one. Otherwise, it derives the base URL from the spec:
31
+
32
+ - OpenAPI 3.x: the first usable `servers` entry
33
+ - Swagger 2.0: `schemes`, `host`, and `basePath`
34
+
35
+ Use `baseUrl` when the spec is missing server data, points at the wrong environment, uses a relative server URL you do not want, or needs to be pinned for this agent.
36
+
37
+ ```ts title="agent/connections/crm.ts"
38
+ import { defineOpenAPIConnection } from "eve/connections";
39
+
40
+ export default defineOpenAPIConnection({
41
+ spec: "https://api.example.com/openapi.json",
42
+ baseUrl: "https://api.example.com",
43
+ description: "CRM accounts, contacts, and opportunities.",
44
+ });
45
+ ```
46
+
47
+ ## Use Vercel Connect for OAuth
48
+
49
+ For OAuth-backed APIs, prefer [Vercel Connect](https://vercel.com/docs/connect). Connect owns browser consent, encrypted token storage, refresh, and project access. The `connect()` helper from `@vercel/connect/eve` plugs that lifecycle into eve's connection auth, so tokens never reach model context or conversation history.
50
+
51
+ Create or attach a connector for the API provider from the Vercel project or agent app that will run the connection:
52
+
53
+ ```bash
54
+ npm install @vercel/connect
55
+ vercel link
56
+ vercel connect create github --name github
57
+ vercel connect attach <connector-uid> --yes
58
+ vercel env pull
59
+ ```
60
+
61
+ Then use the connector UID returned by the CLI:
62
+
63
+ ```ts title="agent/connections/github.ts"
64
+ import { connect } from "@vercel/connect/eve";
65
+ import { defineOpenAPIConnection } from "eve/connections";
66
+
67
+ export default defineOpenAPIConnection({
68
+ spec: "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json",
69
+ baseUrl: "https://api.github.com",
70
+ description: "GitHub repositories, issues, pull requests, and users.",
71
+ auth: connect("github/github"),
72
+ });
73
+ ```
74
+
75
+ By default, `connect("...")` is user-scoped. The active eve session must have a user principal from route auth or from a platform channel before the first operation call. If the API should act as the agent itself, make the connector app-scoped:
76
+
77
+ ```ts
78
+ auth: connect({ connector: "github/github", principalType: "app" });
79
+ ```
80
+
81
+ Use `tokenParams` on `connect(...)` when the provider requires explicit OAuth scopes, audiences, resource indicators, or authorization details.
82
+
83
+ ## Static tokens and headers
84
+
85
+ Use `auth.getToken` when you already have a bearer token, API key, service account token, or out-of-band OAuth flow. eve sends the returned token as `Authorization: Bearer <token>`.
86
+
87
+ ```ts title="agent/connections/crm.ts"
88
+ import { defineOpenAPIConnection } from "eve/connections";
89
+
90
+ export default defineOpenAPIConnection({
91
+ spec: "https://api.example.com/openapi.json",
92
+ baseUrl: "https://api.example.com",
93
+ description: "CRM accounts, contacts, and opportunities.",
94
+ auth: {
95
+ getToken: async () => ({ token: process.env.CRM_TOKEN! }),
96
+ },
97
+ });
98
+ ```
99
+
100
+ Use `headers` for non-Bearer auth schemes, version headers, or tenant routing:
101
+
102
+ ```ts title="agent/connections/notion.ts"
103
+ import { defineOpenAPIConnection } from "eve/connections";
104
+
105
+ export default defineOpenAPIConnection({
106
+ spec: "https://developers.notion.com/openapi.json",
107
+ baseUrl: "https://api.notion.com",
108
+ description: "Notion pages, databases, comments, and users.",
109
+ headers: {
110
+ Authorization: `Bearer ${process.env.NOTION_API_KEY!}`,
111
+ "Notion-Version": "2022-06-28",
112
+ },
113
+ });
114
+ ```
23
115
 
24
- The file path provides the connection name. `agent/connections/petstore.ts` registers as `petstore`, so operation tools are qualified under `petstore__`.
116
+ `auth` and `headers` can also be functions that receive the active session context. Use that when credentials, tenants, or routing depend on the caller.
25
117
 
26
- ## OpenAPI fields
118
+ ## Operation filters
27
119
 
28
- OpenAPI connections use the shared connection fields from [Connections](/docs/connections), plus two OpenAPI-specific fields:
120
+ Most OpenAPI specs describe far more than the model should use. Narrow generated tools with exactly one of `operations.allow` or `operations.block`:
29
121
 
30
- | Field | Purpose |
31
- | ------------ | ------------------------------------------------------------------------------------------------------------- |
32
- | `baseUrl` | Base URL operation paths resolve against. Optional; defaults to the document's first usable `servers` entry. |
33
- | `operations` | Filter keyed on `operationId` (`allow` or `block`). Mirrors `tools` on MCP connections, but names operations. |
122
+ ```ts title="agent/connections/petstore.ts"
123
+ import { defineOpenAPIConnection } from "eve/connections";
124
+
125
+ export default defineOpenAPIConnection({
126
+ spec: "https://petstore3.swagger.io/api/v3/openapi.json",
127
+ description: "Pet store inventory and orders.",
128
+ auth: { getToken: async () => ({ token: process.env.PETSTORE_TOKEN! }) },
129
+ operations: { allow: ["getInventory", "placeOrder"] },
130
+ });
131
+ ```
34
132
 
35
- Use `baseUrl` when the spec's `servers` list is absent, points at the wrong environment, or needs to be pinned for this agent.
133
+ Filters match `operationId`. If an operation does not declare one, use the deterministic name eve derives from the method and path.
36
134
 
37
135
  ## Path parameters
38
136
 
39
- When an operation path contains dynamic segments, declare each segment as an OpenAPI path parameter. eve exposes path, query, header, and cookie parameters as top-level tool inputs, then substitutes `in: "path"` values into the matching `{name}` placeholder before making the request.
137
+ When an operation path contains dynamic segments, the spec must declare matching OpenAPI path parameters. eve exposes path, query, header, and cookie parameters as top-level tool inputs, then substitutes `in: "path"` values into the matching `{name}` placeholder before making the request.
40
138
 
41
139
  ```ts title="agent/connections/cart.ts"
42
140
  import { defineOpenAPIConnection } from "eve/connections";
@@ -48,12 +146,18 @@ export default defineOpenAPIConnection({
48
146
  openapi: "3.0.3",
49
147
  info: { title: "Cart API", version: "1.0.0" },
50
148
  paths: {
51
- "/api/{dynamicsegment}/somethingelse": {
149
+ "/api/{cartId}/items/{itemId}": {
52
150
  get: {
53
- operationId: "getSomethingElse",
151
+ operationId: "getCartItem",
54
152
  parameters: [
55
153
  {
56
- name: "dynamicsegment",
154
+ name: "cartId",
155
+ in: "path",
156
+ required: true,
157
+ schema: { type: "string" },
158
+ },
159
+ {
160
+ name: "itemId",
57
161
  in: "path",
58
162
  required: true,
59
163
  schema: { type: "string" },
@@ -67,37 +171,30 @@ export default defineOpenAPIConnection({
67
171
  });
68
172
  ```
69
173
 
70
- The parameter `name` must exactly match the placeholder inside the path. If the spec omits the `in: "path"` parameter, the generated tool has no input for that segment and eve cannot fill it in from query parameters.
174
+ The parameter `name` must exactly match the placeholder inside the path. If the spec omits an `in: "path"` parameter, the generated tool has no input for that segment and eve cannot fill it in from query parameters.
71
175
 
72
- ## Operation filters
176
+ ## Approval gates
73
177
 
74
- To narrow which generated tools the model sees, set exactly one of `operations.allow` or `operations.block`:
178
+ Generated operation tools can mutate state just like authored tools. Add an approval gate when operations can create, modify, delete, message, transmit, purchase, or access sensitive data:
75
179
 
76
- ```ts title="agent/connections/petstore.ts"
180
+ ```ts title="agent/connections/crm.ts"
77
181
  import { defineOpenAPIConnection } from "eve/connections";
182
+ import { once } from "eve/tools/approval";
78
183
 
79
184
  export default defineOpenAPIConnection({
80
- spec: "https://petstore3.swagger.io/api/v3/openapi.json",
81
- description: "Pet store inventory and orders.",
82
- auth: { getToken: async () => ({ token: process.env.PETSTORE_TOKEN! }) },
83
- operations: { allow: ["getInventory", "placeOrder"] },
185
+ spec: "https://api.example.com/openapi.json",
186
+ baseUrl: "https://api.example.com",
187
+ description: "CRM accounts, contacts, and opportunities.",
188
+ auth: { getToken: async () => ({ token: process.env.CRM_TOKEN! }) },
189
+ approval: once(),
84
190
  });
85
191
  ```
86
192
 
87
- Filters match `operationId`. If an operation does not declare one, use the deterministic name eve derives from the method and path.
88
-
89
- ## Auth, headers, and approval
90
-
91
- OpenAPI connections support the shared connection options:
92
-
93
- - `auth` for static tokens, Vercel Connect, self-hosted interactive OAuth, or a per-caller provider resolver.
94
- - `headers` for static or per-caller API-key schemes and extra server configuration.
95
- - `approval` for human-in-the-loop gates before generated operation tools run.
96
-
97
- See [Connections](/docs/connections) for the shared auth, headers, and approval shapes.
193
+ Combine `approval` with `operations.allow` for the smallest practical surface.
98
194
 
99
195
  ## What to read next
100
196
 
197
+ - [Connections](../connections): shared auth, headers, approval, and per-caller patterns.
101
198
  - [MCP connections](./mcp): connect to remote MCP servers.
102
- - [Auth & route protection](../guides/auth-and-route-protection): the full interactive-OAuth flow with Vercel Connect.
199
+ - [Auth & route protection](../guides/auth-and-route-protection): route auth and the full interactive OAuth lifecycle.
103
200
  - [Security model](../concepts/security-model): how connection credentials stay out of the model's reach.
@@ -110,7 +110,7 @@ import { defineMcpClientConnection } from "eve/connections";
110
110
  import { once } from "eve/tools/approval";
111
111
 
112
112
  export default defineMcpClientConnection({
113
- url: "https://mcp.linear.app/sse",
113
+ url: "https://mcp.linear.app/mcp",
114
114
  description: "Linear workspace.",
115
115
  auth: { getToken: async () => ({ token: process.env.LINEAR_API_TOKEN! }) },
116
116
  approval: once(),
@@ -130,7 +130,7 @@ import { connect } from "@vercel/connect/eve";
130
130
  import { defineMcpClientConnection } from "eve/connections";
131
131
 
132
132
  export default defineMcpClientConnection({
133
- url: "https://mcp.linear.app/sse",
133
+ url: "https://mcp.linear.app/mcp",
134
134
  description: "Linear workspace: issues, projects, cycles, and comments.",
135
135
  auth: connect("linear/myagent"),
136
136
  });
@@ -147,7 +147,7 @@ import { connect } from "@vercel/connect/eve";
147
147
  import { defineMcpClientConnection } from "eve/connections";
148
148
 
149
149
  export default defineMcpClientConnection({
150
- url: "https://mcp.linear.app/sse",
150
+ url: "https://mcp.linear.app/mcp",
151
151
  description: "Linear workspace: issues, projects, cycles, and comments.",
152
152
  auth: connect({ connector: "linear/myagent", principalType: "app" }),
153
153
  });
@@ -176,7 +176,7 @@ import {
176
176
  } from "eve/connections";
177
177
 
178
178
  export default defineMcpClientConnection({
179
- url: "https://mcp.linear.app/sse",
179
+ url: "https://mcp.linear.app/mcp",
180
180
  description: "Linear workspace.",
181
181
  auth: defineInteractiveAuthorization<{ verifier: string }>({
182
182
  // Probed before every tool call. Return a token to run the tool;
@@ -5,6 +5,10 @@ description: "Resolve tenant policy asynchronously for authored tools, OpenAPI o
5
5
 
6
6
  eve's `approval` field is an async policy hook. It receives the active session, qualified tool name, tool input, and previously approved tools. That is enough to ask your application whether this tenant should allow, deny, or require human confirmation for any authored or connection tool.
7
7
 
8
+ Use this with [multi-tenant outbound auth](./multi-tenant-auth) when your own
9
+ API key, JWT, or app session establishes the tenant and the connection
10
+ credential is selected from your credential store rather than OAuth.
11
+
8
12
  The pattern has two pieces:
9
13
 
10
14
  1. one adapter translates eve's approval context into an application policy request;