eve 0.15.5 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/src/channel/compiled-channel.d.ts +2 -0
- package/dist/src/channel/cors.d.ts +43 -0
- package/dist/src/channel/cors.js +1 -0
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/dev/tui/agent-header.js +1 -1
- package/dist/src/cli/dev/tui/agent-info-probe.d.ts +16 -0
- package/dist/src/cli/dev/tui/agent-info-probe.js +1 -0
- package/dist/src/cli/dev/tui/blocks.js +8 -8
- package/dist/src/cli/dev/tui/mcp-connection-status.d.ts +19 -0
- package/dist/src/cli/dev/tui/mcp-connection-status.js +1 -0
- package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
- package/dist/src/cli/dev/tui/prompt-commands.d.ts +1 -1
- package/dist/src/cli/dev/tui/prompt-commands.js +1 -1
- package/dist/src/cli/dev/tui/remote-connection-probe.js +1 -1
- package/dist/src/cli/dev/tui/runner.d.ts +14 -3
- package/dist/src/cli/dev/tui/runner.js +1 -1
- package/dist/src/cli/dev/tui/setup-commands.d.ts +10 -1
- package/dist/src/cli/dev/tui/setup-commands.js +2 -2
- package/dist/src/cli/dev/tui/setup-flow.d.ts +8 -1
- package/dist/src/cli/dev/tui/setup-issues.d.ts +2 -14
- package/dist/src/cli/dev/tui/setup-issues.js +1 -1
- package/dist/src/cli/dev/tui/setup-panel.d.ts +14 -8
- package/dist/src/cli/dev/tui/setup-panel.js +3 -3
- package/dist/src/cli/dev/tui/terminal-renderer.js +6 -6
- package/dist/src/cli/dev/tui/tui-prompter.js +1 -1
- package/dist/src/cli/dev/tui/tui.d.ts +2 -3
- package/dist/src/cli/dev/tui/tui.js +1 -1
- package/dist/src/cli/dev/ui-options.d.ts +21 -0
- package/dist/src/cli/dev/ui-options.js +1 -0
- package/dist/src/cli/run.d.ts +6 -42
- package/dist/src/cli/run.js +2 -2
- package/dist/src/compiled/.vendor-stamp.json +1 -1
- package/dist/src/compiled/@ai-sdk/mcp/index.d.ts +71 -1
- package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
- package/dist/src/compiler/manifest.d.ts +8 -2
- package/dist/src/compiler/manifest.js +1 -1
- package/dist/src/compiler/normalize-channel.js +1 -1
- package/dist/src/context/providers/connection-key.d.ts +9 -0
- package/dist/src/context/providers/connection-key.js +1 -0
- package/dist/src/context/providers/connection.d.ts +1 -9
- package/dist/src/context/providers/connection.js +1 -1
- package/dist/src/harness/emission.d.ts +3 -3
- package/dist/src/harness/emission.js +1 -1
- package/dist/src/harness/input-requests.d.ts +1 -0
- package/dist/src/harness/input-requests.js +1 -1
- package/dist/src/harness/stream-actions.d.ts +17 -0
- package/dist/src/harness/stream-actions.js +1 -0
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/nitro/host/channel-routes.d.ts +2 -0
- package/dist/src/internal/nitro/host/channel-routes.js +5 -3
- package/dist/src/internal/nitro/host/configure-nitro-routes.js +2 -2
- package/dist/src/internal/nitro/host/dev-authored-source-watcher.d.ts +1 -0
- package/dist/src/internal/nitro/host/dev-authored-source-watcher.js +1 -1
- package/dist/src/internal/nitro/host/start-development-server.d.ts +8 -0
- package/dist/src/internal/nitro/host/start-development-server.js +2 -2
- package/dist/src/internal/nitro/host.d.ts +1 -1
- package/dist/src/internal/nitro/host.js +1 -1
- package/dist/src/internal/nitro/routes/channel-dispatch.js +1 -1
- package/dist/src/internal/nitro/routes/dev-runtime-artifacts.d.ts +0 -3
- package/dist/src/internal/nitro/routes/dev-runtime-artifacts.js +1 -1
- package/dist/src/internal/nitro/routes/info.js +1 -1
- package/dist/src/internal/vercel/project-link.d.ts +10 -0
- package/dist/src/internal/vercel/project-link.js +1 -0
- package/dist/src/packages/eve-catalog/src/index.js +1 -1
- package/dist/src/public/channels/auth.d.ts +26 -13
- package/dist/src/public/channels/auth.js +1 -1
- package/dist/src/public/channels/eve.d.ts +31 -0
- package/dist/src/public/channels/eve.js +1 -1
- package/dist/src/public/channels/github/githubChannel.js +1 -1
- package/dist/src/public/channels/github/inbound-types.d.ts +189 -0
- package/dist/src/public/channels/github/inbound-types.js +1 -0
- package/dist/src/public/channels/github/inbound.d.ts +2 -190
- package/dist/src/public/channels/github/inbound.js +2 -2
- package/dist/src/public/channels/index.d.ts +1 -1
- package/dist/src/public/definitions/defineChannel.d.ts +10 -0
- package/dist/src/public/definitions/defineChannel.js +1 -1
- package/dist/src/runtime/agent/mock-model-adapter.d.ts +3 -2
- package/dist/src/runtime/agent/mock-model-adapter.js +1 -1
- package/dist/src/runtime/connections/principal.d.ts +2 -0
- package/dist/src/runtime/connections/principal.js +1 -1
- package/dist/src/runtime/connections/scoped-authorization.d.ts +9 -0
- package/dist/src/runtime/connections/scoped-authorization.js +1 -1
- package/dist/src/runtime/connections/types.d.ts +8 -5
- package/dist/src/runtime/framework-channels/index.d.ts +1 -6
- package/dist/src/runtime/framework-channels/index.js +1 -1
- package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -1
- package/dist/src/runtime/framework-tools/index.d.ts +1 -1
- package/dist/src/runtime/framework-tools/index.js +1 -1
- package/dist/src/runtime/framework-tools/skill.js +1 -1
- package/dist/src/runtime/governance/auth/oidc.js +1 -1
- package/dist/src/runtime/governance/auth/types.d.ts +14 -10
- package/dist/src/runtime/governance/auth/vercel-oidc-project.d.ts +12 -0
- package/dist/src/runtime/governance/auth/vercel-oidc-project.js +1 -0
- package/dist/src/runtime/resolve-channel.js +1 -1
- package/dist/src/runtime/types.d.ts +2 -0
- package/dist/src/services/dev-client/client-options.d.ts +5 -0
- package/dist/src/services/dev-client/client-options.js +1 -1
- package/dist/src/services/dev-client/request-headers.d.ts +8 -0
- package/dist/src/services/dev-client/request-headers.js +1 -1
- package/dist/src/services/dev-client/runtime-artifacts.d.ts +1 -0
- package/dist/src/services/dev-client/runtime-artifacts.js +1 -1
- package/dist/src/services/dev-client.d.ts +8 -0
- package/dist/src/services/dev-client.js +1 -1
- package/dist/src/setup/boxes/add-connections.d.ts +10 -8
- package/dist/src/setup/boxes/add-connections.js +1 -1
- package/dist/src/setup/boxes/resolve-provisioning.d.ts +1 -0
- package/dist/src/setup/boxes/resolve-provisioning.js +1 -1
- package/dist/src/setup/boxes/select-connections.js +1 -1
- package/dist/src/setup/cli/channel-setup-prompter.d.ts +9 -2
- package/dist/src/setup/cli/channel-setup-prompter.js +1 -1
- package/dist/src/setup/cli/index.d.ts +1 -1
- package/dist/src/setup/cli/option-row.d.ts +2 -0
- package/dist/src/setup/cli/option-row.js +1 -1
- package/dist/src/setup/cli/prompt-ui.d.ts +2 -0
- package/dist/src/setup/cli/select-state.js +1 -1
- package/dist/src/setup/connection-connector.d.ts +26 -52
- package/dist/src/setup/connection-connector.js +2 -1
- package/dist/src/setup/flows/channels.js +1 -1
- package/dist/src/setup/flows/connections.d.ts +37 -0
- package/dist/src/setup/flows/connections.js +3 -0
- package/dist/src/setup/flows/link.d.ts +1 -0
- package/dist/src/setup/flows/link.js +1 -1
- package/dist/src/setup/project-resolution.d.ts +2 -8
- package/dist/src/setup/project-resolution.js +1 -1
- package/dist/src/setup/prompter.d.ts +2 -0
- package/dist/src/setup/scaffold/connections/catalog.d.ts +10 -5
- package/dist/src/setup/scaffold/connections/catalog.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +11 -11
- package/dist/src/setup/scaffold/create/web-template.d.ts +1 -1
- package/dist/src/setup/scaffold/create/web-template.js +2 -2
- package/dist/src/setup/scaffold/index.d.ts +1 -1
- package/dist/src/setup/scaffold/index.js +1 -1
- package/dist/src/setup/scaffold/update/connections.d.ts +6 -0
- package/dist/src/setup/scaffold/update/connections.js +3 -3
- package/docs/channels/custom.mdx +19 -0
- package/docs/channels/eve.mdx +26 -3
- package/docs/concepts/sessions-runs-and-streaming.md +3 -1
- package/docs/connections/mcp.mdx +161 -19
- package/docs/connections/openapi.mdx +133 -36
- package/docs/connections/overview.mdx +4 -4
- package/docs/guides/auth-and-route-protection.md +5 -5
- package/docs/guides/dev-tui.md +11 -4
- package/docs/guides/frontend/nextjs.mdx +2 -2
- package/docs/guides/frontend/nuxt.mdx +2 -2
- package/docs/guides/frontend/sveltekit.mdx +2 -2
- package/docs/patterns/multi-tenant-approvals.md +4 -0
- package/docs/patterns/multi-tenant-auth.md +156 -37
- package/docs/reference/cli.md +3 -1
- package/docs/tools/human-in-the-loop.md +3 -1
- package/docs/tutorial/ship-it.mdx +1 -1
- package/docs/tutorial/team-playbooks.mdx +1 -1
- package/package.json +2 -2
- package/dist/src/internal/nitro/host/dev-rebuild-registry.d.ts +0 -5
- package/dist/src/internal/nitro/host/dev-rebuild-registry.js +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{endpointForProtocol}from"../connections/catalog.js";import{pathExists,writeTextFile}from"../files.js";import{patchPackageJson}from"./package-json.js";import{resolveVersionToken}from"../version-tokens.js";import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{join}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const CONNECT_PACKAGE_NAME=`@vercel/connect`,USER_AUTHORED_CONNECTION_DIR=`agent/connections`;function resolveAuth(e){return e.auth??{kind:`none`}}function envKeysForAuth(e){switch(e.kind){case`bearer-env`:return[e.envVar];case`header`:return e.headers.map(e=>e.envVar);case`connect`:case`none`:return[]}}function authBlock(e){switch(e.kind){case`connect`:return` auth: connect("${e.connector}"),\n`;case`bearer-env`:return` auth: { getToken: async () => ({ token: process.env.${e.envVar}! }) },\n`;case`header`:return` headers: () => ({\n${e.headers.map(e=>` "${e.header}": process.env.${e.envVar}!,`).join(`
|
|
1
|
+
import{endpointForProtocol}from"../connections/catalog.js";import{pathExists,writeTextFile}from"../files.js";import{patchPackageJson}from"./package-json.js";import{resolveVersionToken}from"../version-tokens.js";import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{join}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,CONNECT_PACKAGE_NAME=`@vercel/connect`,USER_AUTHORED_CONNECTION_DIR=`agent/connections`;function resolveAuth(e){return e.auth??{kind:`none`}}function envKeysForAuth(e){switch(e.kind){case`bearer-env`:return[e.envVar];case`header`:return e.headers.map(e=>e.envVar);case`connect`:case`none`:return[]}}function authBlock(e){switch(e.kind){case`connect`:return` auth: connect("${e.connector}"),\n`;case`bearer-env`:return` auth: { getToken: async () => ({ token: process.env.${e.envVar}! }) },\n`;case`header`:return` headers: () => ({\n${e.headers.map(e=>` "${e.header}": process.env.${e.envVar}!,`).join(`
|
|
2
2
|
`)}\n }),\n`;case`none`:return``}}function renderMcpTemplate(e,t,n){return`${n.kind===`connect`?`import { connect } from "@vercel/connect/eve";
|
|
3
3
|
import { defineMcpClientConnection } from "eve/connections";
|
|
4
4
|
`:`import { defineMcpClientConnection } from "eve/connections";
|
|
@@ -15,7 +15,7 @@ export default defineOpenAPIConnection({
|
|
|
15
15
|
spec: "${e.spec}",
|
|
16
16
|
${i} description: "${t}",
|
|
17
17
|
${authBlock(n)}});
|
|
18
|
-
`}function renderTemplate(e,t,n,r){return e===`mcp`?renderMcpTemplate(t,n,r):renderOpenApiTemplate(t,n,r)}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function ensureConnectDependency(e,n){if(!await pathExists(e))return[];let i=JSON.parse(await readFile(e,`utf8`));return(isJsonObject(i)&&isJsonObject(i.dependencies)?i.dependencies[CONNECT_PACKAGE_NAME]:void 0)===n?[]:(await patchPackageJson(e,{dependencies:{[CONNECT_PACKAGE_NAME]:n}}),[{path:e,dependencies:[CONNECT_PACKAGE_NAME],devDependencies:[],scripts:[]}])}function envKeyPresent(e,t){return RegExp(`^\\s*(?:export\\s+)?${t}\\s*=`,`m`).test(e)}async function seedEnvPlaceholders(e,n){if(n.length===0)return[];let r=``;await pathExists(e)&&(r=await readFile(e,`utf8`));let i=n.filter(e=>!envKeyPresent(r,e));if(i.length===0)return[];let a=`${r.length>0&&!r.endsWith(`
|
|
18
|
+
`}function renderTemplate(e,t,n,r){return e===`mcp`?renderMcpTemplate(t,n,r):renderOpenApiTemplate(t,n,r)}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function ensureConnectDependency(e,n){if(!await pathExists(e))return[];let i=JSON.parse(await readFile(e,`utf8`));return(isJsonObject(i)&&isJsonObject(i.dependencies)?i.dependencies[CONNECT_PACKAGE_NAME]:void 0)===n?[]:(await patchPackageJson(e,{dependencies:{[CONNECT_PACKAGE_NAME]:n}}),[{path:e,dependencies:[CONNECT_PACKAGE_NAME],devDependencies:[],scripts:[]}])}async function ensureConnectionDependencies(e){let t=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??DEFAULT_CONNECT_PACKAGE_VERSION);return ensureConnectDependency(join(e.projectRoot,`package.json`),t)}function envKeyPresent(e,t){return RegExp(`^\\s*(?:export\\s+)?${t}\\s*=`,`m`).test(e)}async function seedEnvPlaceholders(e,n){if(n.length===0)return[];let r=``;await pathExists(e)&&(r=await readFile(e,`utf8`));let i=n.filter(e=>!envKeyPresent(r,e));if(i.length===0)return[];let a=`${r.length>0&&!r.endsWith(`
|
|
19
19
|
`)?`
|
|
20
20
|
`:``}${i.map(e=>`${e}=`).join(`
|
|
21
|
-
`)}\n`;return await writeFile(e,r+a,`utf8`),i}async function ensureConnection(r){let a=r.slug??r.entry.slug,o=resolveAuth(r.entry),s=endpointForProtocol(r.entry,r.protocol);if(s===null)throw Error(`Connection "${a}" is missing a ${r.protocol===`mcp`?`mcp.url`:`openapi.spec`} endpoint for protocol "${r.protocol}".`);let c=join(r.projectRoot,USER_AUTHORED_CONNECTION_DIR,`${a}.ts`),l=envKeysForAuth(o),u=await pathExists(c);if(!r.force&&u)return{slug:a,protocol:r.protocol,action:`skipped`,filePath:c,filesWritten:[],filesSkipped:[c],packageJsonUpdated:[],envKeysAdded:[],envKeysRequired:l};let d=[];if(o.kind===`connect`){let e=resolveVersionToken(`connectPackageVersion`,r.connectPackageVersion
|
|
21
|
+
`)}\n`;return await writeFile(e,r+a,`utf8`),i}async function ensureConnection(r){let a=r.slug??r.entry.slug,o=resolveAuth(r.entry),s=endpointForProtocol(r.entry,r.protocol);if(s===null)throw Error(`Connection "${a}" is missing a ${r.protocol===`mcp`?`mcp.url`:`openapi.spec`} endpoint for protocol "${r.protocol}".`);let c=join(r.projectRoot,USER_AUTHORED_CONNECTION_DIR,`${a}.ts`),l=envKeysForAuth(o),u=await pathExists(c);if(!r.force&&u)return{slug:a,protocol:r.protocol,action:`skipped`,filePath:c,filesWritten:[],filesSkipped:[c],packageJsonUpdated:[],envKeysAdded:[],envKeysRequired:l};let d=[];if(o.kind===`connect`){let e=resolveVersionToken(`connectPackageVersion`,r.connectPackageVersion??DEFAULT_CONNECT_PACKAGE_VERSION);d.push(...await ensureConnectDependency(join(r.projectRoot,`package.json`),e))}await writeTextFile(c,renderTemplate(r.protocol,s,r.entry.description,o),{force:!0});let f=await seedEnvPlaceholders(join(r.projectRoot,`.env.local`),l),p={slug:a,protocol:r.protocol,action:u?`overwritten`:`created`,filePath:c,filesWritten:[c],filesSkipped:[],packageJsonUpdated:d,envKeysAdded:f,envKeysRequired:l};return u&&(p.filesOverwritten=[c]),p}async function listAuthoredConnections(e){let t=join(e,USER_AUTHORED_CONNECTION_DIR),n;try{n=await readdir(t,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let r=[];for(let e of n){if(e.isFile()){let t=getSupportedModuleBaseName(e.name);t!==null&&r.push(t);continue}if(e.isDirectory())try{(await readdir(join(t,e.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&r.push(e.name)}catch{}}return r.sort()}export{ensureConnection,ensureConnectionDependencies,listAuthoredConnections};
|
package/docs/channels/custom.mdx
CHANGED
|
@@ -55,6 +55,25 @@ Declare routes with the `POST()` and `GET()` helpers. Each route handler receive
|
|
|
55
55
|
|
|
56
56
|
Event handlers like `"message.completed"` are declared under the `events` key. They receive `(eventData, channel, ctx)`, where `eventData` is the event payload, `channel` carries platform handles and session continuation operations, and `ctx` is the eve `SessionContext`. Every channel kind shares this signature. The one exception is `session.failed`, which receives only `(eventData, channel)` with no `ctx`.
|
|
57
57
|
|
|
58
|
+
## CORS
|
|
59
|
+
|
|
60
|
+
Custom HTTP channels leave CORS untouched unless you opt in. Pass `cors: true`
|
|
61
|
+
for permissive browser access with preflight handling, or pass a serializable
|
|
62
|
+
CORS options object to narrow origins, methods, and headers:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import { defineChannel, POST } from "eve/channels";
|
|
66
|
+
|
|
67
|
+
export default defineChannel({
|
|
68
|
+
cors: {
|
|
69
|
+
origin: ["https://app.example.com"],
|
|
70
|
+
methods: ["POST"],
|
|
71
|
+
allowHeaders: ["authorization", "content-type"],
|
|
72
|
+
},
|
|
73
|
+
routes: [POST("/message", async () => new Response("ok"))],
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
58
77
|
## WebSocket routes
|
|
59
78
|
|
|
60
79
|
Use `WS()` when a custom channel needs a WebSocket endpoint. The route handler runs once per upgrade request and returns lifecycle hooks for that connection:
|
package/docs/channels/eve.mdx
CHANGED
|
@@ -12,7 +12,7 @@ import { eveChannel } from "eve/channels/eve";
|
|
|
12
12
|
import { localDev, vercelOidc } from "eve/channels/auth";
|
|
13
13
|
|
|
14
14
|
export default eveChannel({
|
|
15
|
-
auth: [
|
|
15
|
+
auth: [vercelOidc(), localDev()],
|
|
16
16
|
});
|
|
17
17
|
```
|
|
18
18
|
|
|
@@ -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:
|
|
@@ -54,7 +77,7 @@ The `auth` option decides who can call these routes. The built-in helpers cover
|
|
|
54
77
|
|
|
55
78
|
Neither admits browser users or external clients in production. For a public app, wire the channel to your own auth (Clerk, Auth.js, your own OIDC/JWT verification, an API-key verifier, or any custom `AuthFn`). Vercel OIDC is optional; use it only when Vercel-issued deployment tokens are part of your trust model.
|
|
56
79
|
|
|
57
|
-
`eve init` scaffolds an `agent/channels/eve.ts` with a production placeholder so you replace it before going live. The generated channel
|
|
80
|
+
`eve init` scaffolds an `agent/channels/eve.ts` with a production placeholder so you replace it before going live. The generated channel checks Vercel OIDC before falling back to localhost access, and includes `placeholderAuth()`, which returns a setup-focused 401 in production until you swap it for real auth. Delete the file and eve falls back to `[vercelOidc(), localDev()]`, which still does not admit browser users in production.
|
|
58
81
|
|
|
59
82
|
For the full auth model and helper list, see [Auth & route protection](../guides/auth-and-route-protection).
|
|
60
83
|
|
|
@@ -67,7 +90,7 @@ import { eveChannel, defaultEveAuth } from "eve/channels/eve";
|
|
|
67
90
|
import { localDev, vercelOidc } from "eve/channels/auth";
|
|
68
91
|
|
|
69
92
|
export default eveChannel({
|
|
70
|
-
auth: [
|
|
93
|
+
auth: [vercelOidc(), localDev()],
|
|
71
94
|
onMessage(ctx, message) {
|
|
72
95
|
const callerId = ctx.eve.caller?.principalId ?? "anonymous";
|
|
73
96
|
return {
|
|
@@ -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
|
|
@@ -108,7 +110,7 @@ Start with the [TypeScript SDK](../guides/client/overview) guide. It covers basi
|
|
|
108
110
|
curl http://127.0.0.1:3000/eve/v1/info
|
|
109
111
|
```
|
|
110
112
|
|
|
111
|
-
The route uses the same default auth chain as the eve channel (`[
|
|
113
|
+
The route uses the same default auth chain as the eve channel (`[vercelOidc(), localDev()]`). A local Vercel OIDC bearer takes precedence; other local requests fall back to development access. A deployed Vercel target requires a valid OIDC bearer, with a same-project bypass for in-deployment callers. See [auth & route protection](../guides/auth-and-route-protection).
|
|
112
114
|
|
|
113
115
|
## Dispatch order
|
|
114
116
|
|
package/docs/connections/mcp.mdx
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
`
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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/
|
|
39
|
-
description: "Linear: read
|
|
40
|
-
auth:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
##
|
|
118
|
+
## Operation filters
|
|
27
119
|
|
|
28
|
-
OpenAPI
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
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,
|
|
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/{
|
|
149
|
+
"/api/{cartId}/items/{itemId}": {
|
|
52
150
|
get: {
|
|
53
|
-
operationId: "
|
|
151
|
+
operationId: "getCartItem",
|
|
54
152
|
parameters: [
|
|
55
153
|
{
|
|
56
|
-
name: "
|
|
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
|
|
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
|
-
##
|
|
176
|
+
## Approval gates
|
|
73
177
|
|
|
74
|
-
|
|
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/
|
|
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://
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
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
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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;
|