@agent-native/core 0.17.2 → 0.18.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.
Files changed (80) hide show
  1. package/dist/action.d.ts +27 -0
  2. package/dist/action.d.ts.map +1 -1
  3. package/dist/action.js +2 -0
  4. package/dist/action.js.map +1 -1
  5. package/dist/agent/production-agent.d.ts +4 -0
  6. package/dist/agent/production-agent.d.ts.map +1 -1
  7. package/dist/agent/production-agent.js.map +1 -1
  8. package/dist/cli/index.js +16 -0
  9. package/dist/cli/index.js.map +1 -1
  10. package/dist/cli/mcp.d.ts +16 -0
  11. package/dist/cli/mcp.d.ts.map +1 -0
  12. package/dist/cli/mcp.js +583 -0
  13. package/dist/cli/mcp.js.map +1 -0
  14. package/dist/db/client.d.ts +17 -14
  15. package/dist/db/client.d.ts.map +1 -1
  16. package/dist/db/client.js +31 -27
  17. package/dist/db/client.js.map +1 -1
  18. package/dist/db/create-get-db.js +2 -2
  19. package/dist/db/create-get-db.js.map +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/mcp/build-server.d.ts +152 -0
  24. package/dist/mcp/build-server.d.ts.map +1 -0
  25. package/dist/mcp/build-server.js +349 -0
  26. package/dist/mcp/build-server.js.map +1 -0
  27. package/dist/mcp/builtin-tools.d.ts +39 -0
  28. package/dist/mcp/builtin-tools.d.ts.map +1 -0
  29. package/dist/mcp/builtin-tools.js +401 -0
  30. package/dist/mcp/builtin-tools.js.map +1 -0
  31. package/dist/mcp/index.d.ts +7 -0
  32. package/dist/mcp/index.d.ts.map +1 -1
  33. package/dist/mcp/index.js +8 -0
  34. package/dist/mcp/index.js.map +1 -1
  35. package/dist/mcp/server.d.ts +3 -13
  36. package/dist/mcp/server.d.ts.map +1 -1
  37. package/dist/mcp/server.js +44 -179
  38. package/dist/mcp/server.js.map +1 -1
  39. package/dist/mcp/stdio.d.ts +44 -0
  40. package/dist/mcp/stdio.d.ts.map +1 -0
  41. package/dist/mcp/stdio.js +209 -0
  42. package/dist/mcp/stdio.js.map +1 -0
  43. package/dist/mcp/workspace-resolve.d.ts +68 -0
  44. package/dist/mcp/workspace-resolve.d.ts.map +1 -0
  45. package/dist/mcp/workspace-resolve.js +205 -0
  46. package/dist/mcp/workspace-resolve.js.map +1 -0
  47. package/dist/server/action-discovery.d.ts.map +1 -1
  48. package/dist/server/action-discovery.js +3 -0
  49. package/dist/server/action-discovery.js.map +1 -1
  50. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  51. package/dist/server/agent-chat-plugin.js +1 -0
  52. package/dist/server/agent-chat-plugin.js.map +1 -1
  53. package/dist/server/auth.d.ts +9 -0
  54. package/dist/server/auth.d.ts.map +1 -1
  55. package/dist/server/auth.js +71 -19
  56. package/dist/server/auth.js.map +1 -1
  57. package/dist/server/better-auth-instance.d.ts.map +1 -1
  58. package/dist/server/better-auth-instance.js +15 -10
  59. package/dist/server/better-auth-instance.js.map +1 -1
  60. package/dist/server/core-routes-plugin.d.ts +5 -0
  61. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  62. package/dist/server/core-routes-plugin.js +9 -0
  63. package/dist/server/core-routes-plugin.js.map +1 -1
  64. package/dist/server/deep-link.d.ts +55 -0
  65. package/dist/server/deep-link.d.ts.map +1 -0
  66. package/dist/server/deep-link.js +69 -0
  67. package/dist/server/deep-link.js.map +1 -0
  68. package/dist/server/index.d.ts +2 -0
  69. package/dist/server/index.d.ts.map +1 -1
  70. package/dist/server/index.js +2 -0
  71. package/dist/server/index.js.map +1 -1
  72. package/dist/server/open-route.d.ts +12 -0
  73. package/dist/server/open-route.d.ts.map +1 -0
  74. package/dist/server/open-route.js +159 -0
  75. package/dist/server/open-route.js.map +1 -0
  76. package/dist/server/request-context.d.ts +8 -0
  77. package/dist/server/request-context.d.ts.map +1 -1
  78. package/dist/server/request-context.js.map +1 -1
  79. package/docs/content/external-agents.md +177 -0
  80. package/package.json +1 -1
@@ -0,0 +1,177 @@
1
+ ---
2
+ title: "External Agents"
3
+ description: "Connect Claude Code, Cowork, and Codex to an agent-native app over MCP — with deep links that drop the user back into the running UI and live artifact round-trip."
4
+ ---
5
+
6
+ # External Agents
7
+
8
+ An agent-native app is reachable by any external coding agent — Claude Code (desktop & CLI), Claude Cowork, Codex — over [MCP](/docs/mcp-protocol). External agents are great at producing artifacts (a draft, an event, a dashboard) but they live in a terminal or another app. Without a bridge, the user gets a wall of JSON and has to go find the thing.
9
+
10
+ The external-agent bridge closes the loop: the agent does the work over MCP, then hands the user a single **"Open in <app> →"** link that opens the real app focused on exactly what was produced. It reuses the existing `navigate` / `application_state` contract the UI already drains every 2s (see [Context Awareness](/docs/context-awareness)) — there is no second navigation mechanism.
11
+
12
+ ## Overview {#overview}
13
+
14
+ - **One-command setup** — `agent-native mcp install --client <c>` writes the client config and provisions a token.
15
+ - **`link` builder** — any action that produces or lists a navigable resource returns a deep link; MCP/A2A surfaces auto-append an "Open in … →" link.
16
+ - **`/_agent-native/open` route** — a pure pointer (view + record ids + filters); the record-focusing write is always scoped to the **browser session**, never the agent's token.
17
+ - **Ingest actions** — GET + `readOnly` + `publicAgent` actions let an external agent pull **live** app state into its own context.
18
+ - **Generic cross-app verbs** — a stable verb set (`list_apps`, `open_app`, `ask_app`, `create_workspace_app`, `list_templates`) so an external agent has a predictable surface without guessing per-app action names.
19
+
20
+ ## Connect an external agent {#connect}
21
+
22
+ The framework already mounts an HTTP MCP endpoint at `/_agent-native/mcp` (see [MCP Protocol](/docs/mcp-protocol)). Every `defineAction` is exposed as an MCP tool, plus the `ask-agent` meta-tool that runs the full agent loop (the same entry point [A2A](/docs/a2a-protocol) uses). Hosted apps point an external agent at that URL with a bearer token (`ACCESS_TOKEN`, or an `A2A_SECRET` JWT carrying the caller's `sub` + `org_domain` so tool runs stay tenant-scoped).
23
+
24
+ For local Claude Code / Codex / Cowork, one command writes the client config:
25
+
26
+ ```bash
27
+ agent-native mcp install --client claude-code|claude-code-cli|codex|cowork \
28
+ [--app <id>] [--scope user|project]
29
+ ```
30
+
31
+ It provisions a token (a random `ACCESS_TOKEN` into the workspace `.env` for local dev, or a signed JWT for a detected hosted deployment) and writes an idempotent stdio server entry:
32
+
33
+ - **claude-code / claude-code-cli** — an `mcpServers` entry in `.mcp.json` (project scope, default) or `~/.claude.json` (`--scope user`).
34
+ - **cowork** — the same Claude Code JSON shape in `~/.cowork/mcp.json`.
35
+ - **codex** — an `[mcp_servers.<name>]` block in `~/.codex/config.toml`.
36
+
37
+ The entry runs `agent-native mcp serve --app <id>`, which by default is a **thin stdio proxy** to the running local app's `/_agent-native/mcp` — so the live action registry, HMR, and correct deep links stay the single source of truth. Pass `--standalone` to build the registry in-process instead. When `agent-native mcp install` detects a hosted origin (a non-localhost `APP_URL` / `BETTER_AUTH_URL` / `AGENT_NATIVE_MCP_URL` in the workspace `.env`), it writes an `http` client entry pointing at `<origin>/_agent-native/mcp` with a `Bearer` JWT instead of a stdio entry.
38
+
39
+ Companion subcommands:
40
+
41
+ | Command | What it does |
42
+ | ----------------------------------------- | ------------------------------------------------------------------- |
43
+ | `agent-native mcp serve [--app <id>]` | Run the MCP stdio transport (what client configs spawn). |
44
+ | `agent-native mcp install --client <c>` | Provision a token + write the client's MCP config (idempotent). |
45
+ | `agent-native mcp uninstall --client <c>` | Remove the named MCP entry from a client's config (idempotent). |
46
+ | `agent-native mcp status` | Show resolved MCP URL/port, token state, and per-client entries. |
47
+ | `agent-native mcp token [--rotate]` | Print (or rotate) the local `ACCESS_TOKEN` in the workspace `.env`. |
48
+
49
+ Restart the client after `install` so it picks up the new MCP server.
50
+
51
+ ## The `link` builder {#link-builder}
52
+
53
+ `defineAction` accepts an optional `link` builder. When set, every MCP/A2A result for that tool auto-appends a markdown `[label →](absoluteUrl)` block and a structured `_meta["agent-native/openLink"] = { label, view, webUrl, desktopUrl }`. `tools/list` adds `annotations["agent-native/producesOpenLink"]` and a description suffix so the external agent knows the tool yields an openable link and should surface it.
54
+
55
+ Build the URL with `buildDeepLink(...)` — it is the single source of truth for the open-route format. Never hand-format the `/_agent-native/open` URL.
56
+
57
+ Real example — mail's `manage-draft` (`templates/mail/actions/manage-draft.ts`):
58
+
59
+ ```ts
60
+ import { buildDeepLink } from "@agent-native/core/server";
61
+
62
+ function composeDeepLink(draft: Record<string, string>): string {
63
+ return buildDeepLink({
64
+ app: "mail",
65
+ view: "inbox",
66
+ compose: encodeComposeDraft(draft), // base64url JSON → compose-<id> draft
67
+ });
68
+ }
69
+
70
+ export default defineAction({
71
+ // ...schema, run...
72
+ link: ({ result }) => {
73
+ if (!result || typeof result !== "object") return null;
74
+ const draft = (result as { draft?: Record<string, string> }).draft;
75
+ const id = (result as { id?: string }).id;
76
+ if (!draft || !id) return null;
77
+ return {
78
+ url: composeDeepLink(draft),
79
+ label: "Open draft in Mail",
80
+ view: "inbox",
81
+ };
82
+ },
83
+ });
84
+ ```
85
+
86
+ List/search actions point at a record-focused view the same way — e.g. calendar's `create-event` returns `buildDeepLink({ app: "calendar", view: "calendar", params: { eventId, date } })` with label `"Open event in Calendar"`.
87
+
88
+ ### The `link` contract {#link-contract}
89
+
90
+ The `link` builder is **pure and synchronous — no I/O, no awaits**. It runs best-effort: a throw, `null`, or `undefined` is swallowed and **never** fails the tool call. It only reads the call's `args` and `result`; it must not query the DB, read app-state, or call other actions. Return `null` when there's nothing to open.
91
+
92
+ `buildDeepLink({ app, view, params?, to?, compose? })` returns the app-relative path `/_agent-native/open?app=…&view=…&<recordId>=…`. The MCP layer turns that into an absolute web URL (`toAbsoluteOpenUrl`, using the request origin) and a desktop `agentnative://open?…` URL (`toDesktopOpenUrl`); the markdown link uses the desktop URL when the client signals `target: "desktop"`.
93
+
94
+ ## The `/_agent-native/open` route {#open-route}
95
+
96
+ When the user clicks the link in any browser or inline webview, `GET /_agent-native/open` (`createOpenRouteHandler`, mounted by the core routes plugin):
97
+
98
+ 1. Resolves the **browser** session via `getSession` (the auth guard bypasses the exact path `/_agent-native/open`).
99
+ 2. If unauthenticated, serves the configured login HTML **at the same URL**; the form's success handler reloads `window.location`, re-entering the route authenticated — no `?next=` plumbing.
100
+ 3. Writes the existing one-shot `navigate` application-state command (payload = every non-reserved query param + `view`) scoped to the browser session's email with `requestSource: "deep-link"`, and decodes a `compose` base64url draft into a `compose-<id>` key.
101
+ 4. 302-redirects to a safe same-origin relative path (`to=`, else `/<view>`, else a per-template `resolveOpenPath`), forwarding `f_*` filter params so lists/dashboards open pre-filtered before the `navigate` command is even drained.
102
+
103
+ Cross-origin, scheme-relative `//host`, and control-char redirects are rejected (open-redirect guard). The route can be disabled per app via `disableOpenRoute`.
104
+
105
+ ### The browser-session identity rule {#identity-rule}
106
+
107
+ The link carries **no privileged state** — it is just `view` + record ids + filters. The record-focusing `navigate` write is scoped to whoever is logged into the **browser**, never the external agent's MCP token. So an agent authenticated as one identity can hand a user a link, and when that user clicks it the record opens where _the user_ is logged in. This is what makes the deep link safe to surface in a terminal or chat transcript. See [Context Awareness](/docs/context-awareness) for the `navigate` / `application_state` contract this bridges to.
108
+
109
+ ## Ingest actions {#ingest}
110
+
111
+ An action an external agent reads to pull live app state into its own context must be:
112
+
113
+ ```ts
114
+ export default defineAction({
115
+ description: "…",
116
+ schema: z.object({ id: z.string() }),
117
+ http: { method: "GET" },
118
+ readOnly: true,
119
+ publicAgent: { expose: true, readOnly: true, requiresAuth: true },
120
+ run: async ({ id }) => {
121
+ /* read LIVE state, not the stale DB snapshot column */
122
+ },
123
+ });
124
+ ```
125
+
126
+ `GET` + `readOnly` keeps the action side-effect-free and out of the screen-refresh poll. `publicAgent` is the **explicit opt-in** — a public web route never implies public MCP/A2A exposure; see [Actions](/docs/actions). Design/content ingest actions MUST read **live** state (the Yjs collaborative document, not the stale DB snapshot column) so the external agent sees what the user actually has on screen. Content's `pull-document` flushes any open live collab session to SQL first; design's `get-design-snapshot` returns the live Yjs file contents plus the user's resolved tweak values.
127
+
128
+ ## Generic cross-app verbs + scaffolding {#cross-app}
129
+
130
+ On top of the per-action tools the MCP server exposes a stable verb set so an external agent has a predictable surface without guessing per-app action names:
131
+
132
+ | Tool | Side effects | Returns |
133
+ | ------------------------------------------ | ------------ | ------------------------------------------------------------------------------------ |
134
+ | `list_apps` | none | workspace apps + their dev URLs / running state |
135
+ | `open_app({ app, view, params? })` | none | a `buildDeepLink` URL (surfaces as an "Open …" link) |
136
+ | `ask_app({ app, message })` | agent loop | routes a natural-language task to that app's in-app agent (delegates to `ask-agent`) |
137
+ | `create_workspace_app({ name, template })` | scaffolds | a new app booted via the workspace path, plus its running URL + deep link |
138
+ | `list_templates` | none | the allow-listed templates only |
139
+
140
+ `create_workspace_app` rejects any non-allow-listed template — the public template allow-list in `packages/shared-app-config/templates.ts` is authoritative and CI-guarded; an external agent cannot widen it. A same-named template action overrides a builtin (template-over-core precedence). Disable the whole set with `MCPConfig.builtinCrossAppTools: false`.
141
+
142
+ ## Per-app tour {#tour}
143
+
144
+ Every allow-listed template that produces or lists a navigable resource ships a `link` builder, and the ingest-heavy ones ship a GET + `publicAgent` action:
145
+
146
+ - **Mail** — `manage-draft` returns a `compose`-encoded deep link; clicking it opens the inbox with the draft restored into a `compose-<id>`. `list-emails` / `search-emails` point at a filtered inbox view.
147
+ - **Calendar** — `create-event` returns `buildDeepLink({ app: "calendar", view: "calendar", params: { eventId, date } })`; the click lands on the calendar with that event focused on its date.
148
+ - **Analytics** — `update-dashboard` / `save-analysis` return `buildDeepLink({ app: "analytics", view: "adhoc", params: { dashboardId } })`; the agent builds a dashboard over MCP and hands back "Open dashboard in Analytics".
149
+ - **Design** — `get-design-snapshot` is the GET + `publicAgent` ingest action: it returns the **live** Yjs file contents plus the resolved tweak values so the agent continues from the tuned design, not the original tokens. `apply-tweaks` round-trips back with an "Open design" editor link.
150
+ - **Content** — `pull-document` is the GET + `publicAgent` ingest action: it flushes any open live collaborative session to SQL first so the external agent ingests exactly what the user sees, then surfaces a deep link to the document.
151
+ - **Brain** — `ask-brain` / `search-everything` return a cited answer plus a deep link to the underlying knowledge/capture, so a terminal agent's lookup links straight back into the source in the running app.
152
+
153
+ ## Do / Don't {#do-dont}
154
+
155
+ **Do**
156
+
157
+ - Add a `link` builder to any action that produces or lists a navigable resource (draft, event, dashboard, document).
158
+ - Build the URL with `buildDeepLink(...)` — the single source of truth for the open-route format.
159
+ - Keep `link` pure and synchronous; return `null` when there's nothing to open.
160
+ - Make external-agent ingest actions GET + `readOnly` + `publicAgent`, and read live (Yjs) state, not the stale DB column.
161
+ - Let the open route resolve the browser session; pass record ids as deep-link params and let the UI focus them via the polled `navigate` command.
162
+
163
+ **Don't**
164
+
165
+ - Hand-format the `/_agent-native/open` URL — always go through `buildDeepLink`.
166
+ - Do I/O, awaits, DB reads, or app-state reads inside a `link` builder.
167
+ - Scope the `navigate` write to the agent token, or pass privileged state through the deep link — it's a pure pointer.
168
+ - Invent a new navigation mechanism; bridge to the existing `navigate` / `application_state` contract.
169
+ - Widen the public template allow-list when scaffolding an app from an external agent — the allow-list is authoritative and guarded.
170
+
171
+ ## Related {#related}
172
+
173
+ - [MCP Protocol](/docs/mcp-protocol) — the auto-mounted MCP server and `ask-agent` meta-tool.
174
+ - [MCP Clients](/docs/mcp-clients) — the symmetric direction: your app consuming local/remote MCP servers.
175
+ - [A2A Protocol](/docs/a2a-protocol) — the `ask-agent` meta-tool and JSON-RPC peer calls.
176
+ - [Actions](/docs/actions) — defining actions, `publicAgent`, GET / `readOnly`.
177
+ - [Context Awareness](/docs/context-awareness) — the `navigate` / `application_state` contract the open route bridges to.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.17.2",
3
+ "version": "0.18.1",
4
4
  "type": "module",
5
5
  "description": "Framework for agent-native application development — where AI agents and UI share state via files",
6
6
  "license": "MIT",