@agent-native/core 0.19.0 → 0.19.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/dist/a2a/caller-auth.d.ts +1 -0
- package/dist/a2a/caller-auth.d.ts.map +1 -1
- package/dist/a2a/caller-auth.js +1 -1
- package/dist/a2a/caller-auth.js.map +1 -1
- package/dist/agent/production-agent.d.ts +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +34 -2
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/code-agent-executor.d.ts.map +1 -1
- package/dist/cli/code-agent-executor.js +47 -256
- package/dist/cli/code-agent-executor.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +3 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +4 -4
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +3 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +11 -3
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +4 -1
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/dynamic-suggestions.d.ts +43 -0
- package/dist/client/dynamic-suggestions.d.ts.map +1 -0
- package/dist/client/dynamic-suggestions.js +344 -0
- package/dist/client/dynamic-suggestions.js.map +1 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/settings/SettingsPanel.js +2 -2
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/coding-tools/index.d.ts +31 -0
- package/dist/coding-tools/index.d.ts.map +1 -0
- package/dist/coding-tools/index.js +411 -0
- package/dist/coding-tools/index.js.map +1 -0
- package/dist/mcp/builtin-tools.d.ts.map +1 -1
- package/dist/mcp/builtin-tools.js +85 -26
- package/dist/mcp/builtin-tools.js.map +1 -1
- package/dist/mcp/connect-route.d.ts.map +1 -1
- package/dist/mcp/connect-route.js +148 -42
- package/dist/mcp/connect-route.js.map +1 -1
- package/dist/mcp/org-directory.d.ts +83 -0
- package/dist/mcp/org-directory.d.ts.map +1 -0
- package/dist/mcp/org-directory.js +201 -0
- package/dist/mcp/org-directory.js.map +1 -0
- package/dist/mcp/server.d.ts +38 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +208 -77
- package/dist/mcp/server.js.map +1 -1
- package/dist/scripts/dev/index.d.ts +6 -4
- package/dist/scripts/dev/index.d.ts.map +1 -1
- package/dist/scripts/dev/index.js +28 -13
- package/dist/scripts/dev/index.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +6 -6
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +32 -32
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-teams.js +2 -2
- package/dist/server/agent-teams.js.map +1 -1
- package/dist/server/agents-bundle.d.ts +3 -3
- package/dist/server/agents-bundle.js +5 -5
- package/dist/server/agents-bundle.js.map +1 -1
- package/dist/server/sentry.d.ts.map +1 -1
- package/dist/server/sentry.js +17 -2
- package/dist/server/sentry.js.map +1 -1
- package/docs/content/client.md +15 -0
- package/docs/content/code-agents-ui.md +11 -1
- package/docs/content/drop-in-agent.md +3 -1
- package/docs/content/frames.md +1 -1
- package/docs/content/migration-workbench.md +5 -0
- package/package.json +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -7,98 +7,182 @@ import { createMCPServerForRequest, verifyAuth, getAccessTokens, resolveOrgIdFro
|
|
|
7
7
|
// against `./server.js` exactly as before this refactor.
|
|
8
8
|
export { createMCPServerForRequest, verifyAuth, getAccessTokens, resolveOrgIdFromDomain, buildLinkArtifacts, };
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
|
-
//
|
|
10
|
+
// Runtime detection — Node fast-path vs. web-standard fallback
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Resolve the underlying Node `http` req/res pair if (and only if) we're
|
|
14
|
+
* running on a real Node HTTP server (local dev, `node` Nitro preset). On the
|
|
15
|
+
* web-standard runtime (Nitro 3 / Netlify web runtime, Cloudflare, Deno, Bun)
|
|
16
|
+
* BOTH of these are undefined — that's the signal to take the web fallback
|
|
17
|
+
* instead of returning 501.
|
|
18
|
+
*/
|
|
19
|
+
function getNodeReqRes(event) {
|
|
20
|
+
const e = event;
|
|
21
|
+
const nodeReq = e.node?.req ?? e.req?.runtime?.node?.req;
|
|
22
|
+
const nodeRes = e.node?.res ?? e.req?.runtime?.node?.res;
|
|
23
|
+
return { nodeReq, nodeRes };
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Derive the request origin + the markdown deep-link target from the inbound
|
|
27
|
+
* headers. Identical logic for both the Node and web paths so the absolute
|
|
28
|
+
* deep-link URLs in tool results are computed the same way regardless of
|
|
29
|
+
* runtime.
|
|
30
|
+
*/
|
|
31
|
+
function deriveRequestMeta(event) {
|
|
32
|
+
const forwardedProto = getRequestHeader(event, "x-forwarded-proto");
|
|
33
|
+
const host = getRequestHeader(event, "host");
|
|
34
|
+
const proto = forwardedProto?.split(",")[0]?.trim() ||
|
|
35
|
+
(host && /^(localhost|127\.0\.0\.1)(:|$)/.test(host) ? "http" : "https");
|
|
36
|
+
const origin = host ? `${proto}://${host}` : undefined;
|
|
37
|
+
const targetHeader = getRequestHeader(event, "x-agent-native-open-target")?.toLowerCase();
|
|
38
|
+
const target = targetHeader === "desktop" ||
|
|
39
|
+
targetHeader === "terminal" ||
|
|
40
|
+
targetHeader === "browser"
|
|
41
|
+
? targetHeader
|
|
42
|
+
: undefined;
|
|
43
|
+
return { origin, target };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Reconstruct a Web Standard `Request` for the web-standard MCP transport.
|
|
14
47
|
*
|
|
15
|
-
*
|
|
48
|
+
* On the web runtime h3 v2 exposes the real web `Request` as `event.req`; we
|
|
49
|
+
* prefer it (its `method` / `headers` are exactly what the client sent). But
|
|
50
|
+
* the framework middleware rewrites `event.req.url` when it strips a mount
|
|
51
|
+
* prefix, and the transport reads `req.method` + `req.headers` (never the
|
|
52
|
+
* body — we pass that via `parsedBody`), so we always synthesize a clean
|
|
53
|
+
* `Request` with the verified method + a fresh `Headers` copy. The URL is
|
|
54
|
+
* cosmetic for the SDK (it only does `new URL(req.url)` for `requestInfo`),
|
|
55
|
+
* so a best-effort absolute URL derived from the inbound host is sufficient
|
|
56
|
+
* and never throws.
|
|
57
|
+
*/
|
|
58
|
+
function buildWebRequest(event, method) {
|
|
59
|
+
const src = event.req;
|
|
60
|
+
const headers = new Headers();
|
|
61
|
+
if (src?.headers && typeof src.headers.forEach === "function") {
|
|
62
|
+
src.headers.forEach((value, key) => headers.set(key, value));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
const rawHeaders = event.node?.req?.headers;
|
|
66
|
+
if (rawHeaders) {
|
|
67
|
+
for (const [key, value] of Object.entries(rawHeaders)) {
|
|
68
|
+
if (value == null)
|
|
69
|
+
continue;
|
|
70
|
+
headers.set(key, Array.isArray(value) ? value.join(", ") : value);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// The SDK requires Accept + Content-Type to advertise both JSON and SSE on
|
|
75
|
+
// a POST. Real MCP clients (Claude Code, `agent-native connect`) always
|
|
76
|
+
// send these; we never inject/alter them — if they're absent the SDK
|
|
77
|
+
// returns its spec-mandated 406/415, identical to the Node path.
|
|
78
|
+
const host = headers.get("host") || "localhost";
|
|
79
|
+
const forwardedProto = headers.get("x-forwarded-proto");
|
|
80
|
+
const proto = forwardedProto?.split(",")[0]?.trim() ||
|
|
81
|
+
(/^(localhost|127\.0\.0\.1)(:|$)/.test(host) ? "http" : "https");
|
|
82
|
+
let url = `${proto}://${host}/_agent-native/mcp`;
|
|
83
|
+
try {
|
|
84
|
+
if (src?.url)
|
|
85
|
+
url = new URL(src.url).href;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// keep the synthesized URL
|
|
89
|
+
}
|
|
90
|
+
// No body here on purpose: the JSON-RPC payload is forwarded via the
|
|
91
|
+
// transport's `parsedBody` option (the same mechanism the Node transport
|
|
92
|
+
// uses), so the request stream is never read twice.
|
|
93
|
+
return new Request(url, { method, headers });
|
|
94
|
+
}
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// handleMcpRequest — runtime-agnostic MCP request handler
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
/**
|
|
99
|
+
* Handle a single `{routePrefix}/mcp` request on either runtime.
|
|
16
100
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
101
|
+
* - **Node fast-path** (real Node HTTP server): unchanged — delegate to the
|
|
102
|
+
* SDK's `StreamableHTTPServerTransport.handleRequest(nodeReq, nodeRes,
|
|
103
|
+
* body)`, which writes directly to the Node response (full protocol incl.
|
|
104
|
+
* SSE).
|
|
105
|
+
* - **Web-standard fallback** (Nitro 3 / Netlify web runtime, Cloudflare,
|
|
106
|
+
* Deno, Bun — where there is no Node req/res): build the SAME MCP `Server`
|
|
107
|
+
* from the SAME config + identity, drive it through the SDK's
|
|
108
|
+
* `WebStandardStreamableHTTPServerTransport` (which the Node transport is
|
|
109
|
+
* itself just a thin wrapper around), and return the resulting Web
|
|
110
|
+
* `Response` as a normal h3 return value.
|
|
19
111
|
*
|
|
20
|
-
* Auth
|
|
21
|
-
*
|
|
112
|
+
* Auth, the `runWithRequestContext` identity wrap, the deep-link `_meta` /
|
|
113
|
+
* markdown append, `requestMeta` origin/target derivation and the stateless
|
|
114
|
+
* semantics are IDENTICAL on both paths because both build the same server
|
|
115
|
+
* via `createMCPServerForRequest` and both transports funnel into the same
|
|
116
|
+
* `WebStandardStreamableHTTPServerTransport.handleRequest(webRequest, {
|
|
117
|
+
* parsedBody })` with the same options.
|
|
118
|
+
*
|
|
119
|
+
* Returns:
|
|
120
|
+
* - `undefined` when the request targets a sub-route (so management/status
|
|
121
|
+
* routes mounted under `/_agent-native/mcp/*` handle it themselves) — the
|
|
122
|
+
* h3 mount falls through to the next handler.
|
|
123
|
+
* - a Web `Response` (web fallback) or a string/object (Node path /
|
|
124
|
+
* auth-error path) otherwise. The Node path also sets `_handled` so h3
|
|
125
|
+
* doesn't double-write.
|
|
22
126
|
*/
|
|
23
|
-
export function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
127
|
+
export async function handleMcpRequest(event, config) {
|
|
128
|
+
const pathname = event.url?.pathname || "/";
|
|
129
|
+
const subpath = pathname.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
130
|
+
if (subpath) {
|
|
131
|
+
// Let management/status routes mounted under /_agent-native/mcp/* handle
|
|
132
|
+
// their own requests instead of treating them as MCP protocol traffic.
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
const method = getMethod(event);
|
|
136
|
+
// Auth check — extracts the caller's identity from the JWT (`sub`), or, on
|
|
137
|
+
// the static-token / dev-open path, from the forwarded
|
|
138
|
+
// `X-Agent-Native-Owner-Email` hint the stdio proxy sends (the
|
|
139
|
+
// `agent-native mcp install` flow). Without this the install flow would run
|
|
140
|
+
// every tool unscoped (userEmail === undefined).
|
|
141
|
+
const authHeader = getRequestHeader(event, "authorization");
|
|
142
|
+
const ownerEmailHeader = getRequestHeader(event, "x-agent-native-owner-email");
|
|
143
|
+
const authResult = await verifyAuth(authHeader, ownerEmailHeader);
|
|
144
|
+
if (!authResult.authed) {
|
|
145
|
+
setResponseStatus(event, 401);
|
|
146
|
+
return { error: "Unauthorized" };
|
|
147
|
+
}
|
|
148
|
+
// Stateless mode: only POST is meaningful
|
|
149
|
+
if (method === "DELETE") {
|
|
150
|
+
setResponseStatus(event, 204);
|
|
151
|
+
return "";
|
|
152
|
+
}
|
|
153
|
+
if (method !== "POST" && method !== "GET") {
|
|
154
|
+
setResponseStatus(event, 405);
|
|
155
|
+
return { error: "Method not allowed" };
|
|
156
|
+
}
|
|
157
|
+
// Read body for POST (GET has no body). Read it via the h3 helper exactly
|
|
158
|
+
// once; both transports accept it as a pre-parsed body so the request
|
|
159
|
+
// stream is never consumed twice.
|
|
160
|
+
const body = method === "POST" ? await readBody(event) : undefined;
|
|
161
|
+
// Per-request stateless transport + server. Both runtimes build the SAME
|
|
162
|
+
// server from the SAME config + verified identity + request meta, so
|
|
163
|
+
// tools/list, tools/call, and the deep-link `_meta` are identical.
|
|
164
|
+
const requestMeta = deriveRequestMeta(event);
|
|
165
|
+
const server = await createMCPServerForRequest(config, authResult.identity, requestMeta);
|
|
166
|
+
const { nodeReq, nodeRes } = getNodeReqRes(event);
|
|
167
|
+
if (nodeReq && nodeRes) {
|
|
168
|
+
// ---- Node fast-path (UNCHANGED behavior) --------------------------------
|
|
62
169
|
const { StreamableHTTPServerTransport } = await import("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
63
170
|
const transport = new StreamableHTTPServerTransport({
|
|
64
171
|
sessionIdGenerator: undefined, // stateless
|
|
65
172
|
});
|
|
66
|
-
// Derive the running app's origin so relative deep links become
|
|
67
|
-
// absolute URLs the external agent can open (same approach as A2A).
|
|
68
|
-
const forwardedProto = getRequestHeader(event, "x-forwarded-proto");
|
|
69
|
-
const host = getRequestHeader(event, "host");
|
|
70
|
-
const proto = forwardedProto?.split(",")[0]?.trim() ||
|
|
71
|
-
(host && /^(localhost|127\.0\.0\.1)(:|$)/.test(host)
|
|
72
|
-
? "http"
|
|
73
|
-
: "https");
|
|
74
|
-
const origin = host ? `${proto}://${host}` : undefined;
|
|
75
|
-
const targetHeader = getRequestHeader(event, "x-agent-native-open-target")?.toLowerCase();
|
|
76
|
-
const target = targetHeader === "desktop" ||
|
|
77
|
-
targetHeader === "terminal" ||
|
|
78
|
-
targetHeader === "browser"
|
|
79
|
-
? targetHeader
|
|
80
|
-
: undefined;
|
|
81
|
-
const server = await createMCPServerForRequest(config, authResult.identity, { origin, target });
|
|
82
173
|
await server.connect(transport);
|
|
83
|
-
// Delegate to the transport — it writes directly to the Node response.
|
|
84
|
-
// MCP's HTTP transport requires Node streams; this route is Node-only.
|
|
85
|
-
const nodeReq = event.node?.req ?? event.req?.runtime?.node?.req;
|
|
86
|
-
const nodeRes = event.node?.res ?? event.req?.runtime?.node?.res;
|
|
87
|
-
if (!nodeReq || !nodeRes) {
|
|
88
|
-
setResponseStatus(event, 501);
|
|
89
|
-
return { error: "MCP requires Node runtime" };
|
|
90
|
-
}
|
|
91
174
|
try {
|
|
175
|
+
// The SDK transport writes directly to the Node response. Node-only by
|
|
176
|
+
// construction; we only reach here when real Node req/res exist.
|
|
92
177
|
await transport.handleRequest(nodeReq, nodeRes, body);
|
|
93
178
|
}
|
|
94
179
|
catch (err) {
|
|
95
|
-
// The SDK transport writes directly to the Node response. If the
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
// anything else.
|
|
180
|
+
// The SDK transport writes directly to the Node response. If the socket
|
|
181
|
+
// is already closed/ended (client disconnected, or the host stream
|
|
182
|
+
// layer also flushed), Node throws ERR_STREAM_WRITE_AFTER_END *after*
|
|
183
|
+
// the MCP payload was already delivered correctly. Swallow that benign
|
|
184
|
+
// post-flush write so an external agent disconnecting mid-stream can
|
|
185
|
+
// never take down the server process; rethrow anything else.
|
|
102
186
|
if (err?.code !== "ERR_STREAM_WRITE_AFTER_END")
|
|
103
187
|
throw err;
|
|
104
188
|
if (process.env.DEBUG)
|
|
@@ -106,6 +190,53 @@ export function mountMCP(nitroApp, config, routePrefix = "/_agent-native") {
|
|
|
106
190
|
}
|
|
107
191
|
// Prevent H3 from double-writing the response
|
|
108
192
|
event._handled = true;
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
// ---- Web-standard fallback (Nitro 3 / Netlify web runtime, CF, Deno,
|
|
196
|
+
// Bun) ---------------------------------------------------------------------
|
|
197
|
+
//
|
|
198
|
+
// `StreamableHTTPServerTransport` is itself just a thin wrapper that
|
|
199
|
+
// converts the Node req/res to a web Request/Response and delegates to
|
|
200
|
+
// `WebStandardStreamableHTTPServerTransport.handleRequest(webRequest, {
|
|
201
|
+
// parsedBody })`. Using the web transport directly with the SAME options +
|
|
202
|
+
// the same pre-read `parsedBody` produces byte-identical protocol output
|
|
203
|
+
// (including the deep-link `_meta` built inside createMCPServerForRequest),
|
|
204
|
+
// and works on every web runtime because it returns a Web `Response`
|
|
205
|
+
// (JSON for request/response, or an SSE `ReadableStream` body which h3
|
|
206
|
+
// streams natively).
|
|
207
|
+
const { WebStandardStreamableHTTPServerTransport } = await import("@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js");
|
|
208
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
209
|
+
sessionIdGenerator: undefined, // stateless — same as the Node path
|
|
210
|
+
});
|
|
211
|
+
await server.connect(transport);
|
|
212
|
+
const webRequest = buildWebRequest(event, method);
|
|
213
|
+
// `parsedBody: undefined` would make the SDK try to read `req.json()`; our
|
|
214
|
+
// synthesized request has no body, so only pass the option for POST (where
|
|
215
|
+
// we actually have a parsed body). For GET the transport reads no body.
|
|
216
|
+
const response = await transport.handleRequest(webRequest, method === "POST" ? { parsedBody: body } : undefined);
|
|
217
|
+
return response;
|
|
218
|
+
}
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
// mountMCP — register MCP Streamable HTTP endpoint on H3/Nitro
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
/**
|
|
223
|
+
* Mount an MCP remote server on an H3/Nitro app.
|
|
224
|
+
*
|
|
225
|
+
* Endpoint: `{routePrefix}/mcp` (default `/_agent-native/mcp`)
|
|
226
|
+
*
|
|
227
|
+
* Uses stateless Streamable HTTP transport — no in-memory sessions,
|
|
228
|
+
* compatible with serverless deployments. Runtime-agnostic: a real Node
|
|
229
|
+
* server uses the SDK's Node transport; the web-standard runtime (Nitro 3 /
|
|
230
|
+
* Netlify web runtime, Cloudflare, Deno, Bun) uses the SDK's web-standard
|
|
231
|
+
* transport. Both build the same server and produce identical JSON-RPC
|
|
232
|
+
* output.
|
|
233
|
+
*
|
|
234
|
+
* Auth: Bearer token matching ACCESS_TOKEN/ACCESS_TOKENS or JWT via A2A_SECRET.
|
|
235
|
+
* No auth required when neither is configured (dev mode).
|
|
236
|
+
*/
|
|
237
|
+
export function mountMCP(nitroApp, config, routePrefix = "/_agent-native") {
|
|
238
|
+
getH3App(nitroApp).use(`${routePrefix}/mcp`, defineEventHandler(async (event) => {
|
|
239
|
+
return handleMcpRequest(event, config);
|
|
109
240
|
}));
|
|
110
241
|
if (process.env.DEBUG)
|
|
111
242
|
console.log(`[mcp] Mounted MCP server at ${routePrefix}/mcp (${Object.keys(config.actions).length} tools${config.askAgent ? " + ask-agent" : ""})`);
|
package/dist/mcp/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { getH3App } from \"../server/framework-request-handler.js\";\nimport {\n defineEventHandler,\n setResponseStatus,\n getMethod,\n getRequestHeader,\n} from \"h3\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport {\n createMCPServerForRequest,\n verifyAuth,\n getAccessTokens,\n resolveOrgIdFromDomain,\n buildLinkArtifacts,\n type MCPConfig,\n type MCPCallerIdentity,\n type MCPRequestMeta,\n} from \"./build-server.js\";\n\n// Re-export the shared MCP server builder + types so the stdio transport and\n// any (future) external importer of `@agent-native/core/mcp` keep resolving\n// against `./server.js` exactly as before this refactor.\nexport {\n createMCPServerForRequest,\n verifyAuth,\n getAccessTokens,\n resolveOrgIdFromDomain,\n buildLinkArtifacts,\n};\nexport type { MCPConfig, MCPCallerIdentity, MCPRequestMeta };\n\n// ---------------------------------------------------------------------------\n// mountMCP — register MCP Streamable HTTP endpoint on H3/Nitro\n// ---------------------------------------------------------------------------\n\n/**\n * Mount an MCP remote server on an H3/Nitro app.\n *\n * Endpoint: `{routePrefix}/mcp` (default `/_agent-native/mcp`)\n *\n * Uses stateless Streamable HTTP transport — no in-memory sessions,\n * compatible with serverless deployments.\n *\n * Auth: Bearer token matching ACCESS_TOKEN/ACCESS_TOKENS or JWT via A2A_SECRET.\n * No auth required when neither is configured (dev mode).\n */\nexport function mountMCP(\n nitroApp: any,\n config: MCPConfig,\n routePrefix = \"/_agent-native\",\n): void {\n getH3App(nitroApp).use(\n `${routePrefix}/mcp`,\n defineEventHandler(async (event) => {\n const pathname = event.url?.pathname || \"/\";\n const subpath = pathname.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n if (subpath) {\n // Let management/status routes mounted under /_agent-native/mcp/*\n // handle their own requests instead of treating them as MCP protocol\n // traffic.\n return;\n }\n\n const method = getMethod(event);\n\n // Auth check — extracts the caller's identity from the JWT (`sub`),\n // or, on the static-token / dev-open path, from the forwarded\n // `X-Agent-Native-Owner-Email` hint the stdio proxy sends (the\n // `agent-native mcp install` flow). Without this the install flow\n // would run every tool unscoped (userEmail === undefined).\n const authHeader = getRequestHeader(event, \"authorization\");\n const ownerEmailHeader = getRequestHeader(\n event,\n \"x-agent-native-owner-email\",\n );\n const authResult = await verifyAuth(authHeader, ownerEmailHeader);\n if (!authResult.authed) {\n setResponseStatus(event, 401);\n return { error: \"Unauthorized\" };\n }\n\n // Stateless mode: only POST is meaningful\n if (method === \"DELETE\") {\n setResponseStatus(event, 204);\n return \"\";\n }\n\n if (method === \"GET\") {\n // SSE stream endpoint — not used in stateless mode but the SDK\n // handles it gracefully. Let it through for protocol compliance.\n }\n\n if (method !== \"POST\" && method !== \"GET\") {\n setResponseStatus(event, 405);\n return { error: \"Method not allowed\" };\n }\n\n // Read body for POST (GET has no body)\n const body = method === \"POST\" ? await readBody(event) : undefined;\n\n // Create per-request stateless transport + server\n const { StreamableHTTPServerTransport } =\n await import(\"@modelcontextprotocol/sdk/server/streamableHttp.js\");\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined, // stateless\n });\n // Derive the running app's origin so relative deep links become\n // absolute URLs the external agent can open (same approach as A2A).\n const forwardedProto = getRequestHeader(event, \"x-forwarded-proto\");\n const host = getRequestHeader(event, \"host\");\n const proto =\n forwardedProto?.split(\",\")[0]?.trim() ||\n (host && /^(localhost|127\\.0\\.0\\.1)(:|$)/.test(host)\n ? \"http\"\n : \"https\");\n const origin = host ? `${proto}://${host}` : undefined;\n const targetHeader = getRequestHeader(\n event,\n \"x-agent-native-open-target\",\n )?.toLowerCase();\n const target =\n targetHeader === \"desktop\" ||\n targetHeader === \"terminal\" ||\n targetHeader === \"browser\"\n ? (targetHeader as MCPRequestMeta[\"target\"])\n : undefined;\n\n const server = await createMCPServerForRequest(\n config,\n authResult.identity,\n { origin, target },\n );\n await server.connect(transport);\n\n // Delegate to the transport — it writes directly to the Node response.\n // MCP's HTTP transport requires Node streams; this route is Node-only.\n const nodeReq =\n (event as any).node?.req ?? (event as any).req?.runtime?.node?.req;\n const nodeRes =\n (event as any).node?.res ?? (event as any).req?.runtime?.node?.res;\n if (!nodeReq || !nodeRes) {\n setResponseStatus(event, 501);\n return { error: \"MCP requires Node runtime\" };\n }\n try {\n await transport.handleRequest(nodeReq, nodeRes, body);\n } catch (err: any) {\n // The SDK transport writes directly to the Node response. If the\n // socket is already closed/ended (client disconnected, or the host\n // stream layer also flushed), Node throws ERR_STREAM_WRITE_AFTER_END\n // *after* the MCP payload was already delivered correctly. Swallow\n // that benign post-flush write so an external agent disconnecting\n // mid-stream can never take down the server process; rethrow\n // anything else.\n if (err?.code !== \"ERR_STREAM_WRITE_AFTER_END\") throw err;\n if (process.env.DEBUG)\n console.log(\n \"[mcp] ignored post-flush ERR_STREAM_WRITE_AFTER_END (client disconnected)\",\n );\n }\n\n // Prevent H3 from double-writing the response\n (event as any)._handled = true;\n }),\n );\n\n if (process.env.DEBUG)\n console.log(\n `[mcp] Mounted MCP server at ${routePrefix}/mcp (${Object.keys(config.actions).length} tools${config.askAgent ? \" + ask-agent\" : \"\"})`,\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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","sourcesContent":["import type { H3Event } from \"h3\";\nimport { getH3App } from \"../server/framework-request-handler.js\";\nimport {\n defineEventHandler,\n setResponseStatus,\n getMethod,\n getRequestHeader,\n} from \"h3\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport {\n createMCPServerForRequest,\n verifyAuth,\n getAccessTokens,\n resolveOrgIdFromDomain,\n buildLinkArtifacts,\n type MCPConfig,\n type MCPCallerIdentity,\n type MCPRequestMeta,\n} from \"./build-server.js\";\n\n// Re-export the shared MCP server builder + types so the stdio transport and\n// any (future) external importer of `@agent-native/core/mcp` keep resolving\n// against `./server.js` exactly as before this refactor.\nexport {\n createMCPServerForRequest,\n verifyAuth,\n getAccessTokens,\n resolveOrgIdFromDomain,\n buildLinkArtifacts,\n};\nexport type { MCPConfig, MCPCallerIdentity, MCPRequestMeta };\n\n// ---------------------------------------------------------------------------\n// Runtime detection — Node fast-path vs. web-standard fallback\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the underlying Node `http` req/res pair if (and only if) we're\n * running on a real Node HTTP server (local dev, `node` Nitro preset). On the\n * web-standard runtime (Nitro 3 / Netlify web runtime, Cloudflare, Deno, Bun)\n * BOTH of these are undefined — that's the signal to take the web fallback\n * instead of returning 501.\n */\nfunction getNodeReqRes(event: H3Event): {\n nodeReq: any | undefined;\n nodeRes: any | undefined;\n} {\n const e = event as any;\n const nodeReq = e.node?.req ?? e.req?.runtime?.node?.req;\n const nodeRes = e.node?.res ?? e.req?.runtime?.node?.res;\n return { nodeReq, nodeRes };\n}\n\n/**\n * Derive the request origin + the markdown deep-link target from the inbound\n * headers. Identical logic for both the Node and web paths so the absolute\n * deep-link URLs in tool results are computed the same way regardless of\n * runtime.\n */\nfunction deriveRequestMeta(event: H3Event): MCPRequestMeta {\n const forwardedProto = getRequestHeader(event, \"x-forwarded-proto\");\n const host = getRequestHeader(event, \"host\");\n const proto =\n forwardedProto?.split(\",\")[0]?.trim() ||\n (host && /^(localhost|127\\.0\\.0\\.1)(:|$)/.test(host) ? \"http\" : \"https\");\n const origin = host ? `${proto}://${host}` : undefined;\n const targetHeader = getRequestHeader(\n event,\n \"x-agent-native-open-target\",\n )?.toLowerCase();\n const target =\n targetHeader === \"desktop\" ||\n targetHeader === \"terminal\" ||\n targetHeader === \"browser\"\n ? (targetHeader as MCPRequestMeta[\"target\"])\n : undefined;\n return { origin, target };\n}\n\n/**\n * Reconstruct a Web Standard `Request` for the web-standard MCP transport.\n *\n * On the web runtime h3 v2 exposes the real web `Request` as `event.req`; we\n * prefer it (its `method` / `headers` are exactly what the client sent). But\n * the framework middleware rewrites `event.req.url` when it strips a mount\n * prefix, and the transport reads `req.method` + `req.headers` (never the\n * body — we pass that via `parsedBody`), so we always synthesize a clean\n * `Request` with the verified method + a fresh `Headers` copy. The URL is\n * cosmetic for the SDK (it only does `new URL(req.url)` for `requestInfo`),\n * so a best-effort absolute URL derived from the inbound host is sufficient\n * and never throws.\n */\nfunction buildWebRequest(event: H3Event, method: string): Request {\n const src = (event as any).req as Request | undefined;\n\n const headers = new Headers();\n if (src?.headers && typeof src.headers.forEach === \"function\") {\n src.headers.forEach((value, key) => headers.set(key, value));\n } else {\n const rawHeaders = (event as any).node?.req?.headers as\n | Record<string, string | string[] | undefined>\n | undefined;\n if (rawHeaders) {\n for (const [key, value] of Object.entries(rawHeaders)) {\n if (value == null) continue;\n headers.set(key, Array.isArray(value) ? value.join(\", \") : value);\n }\n }\n }\n\n // The SDK requires Accept + Content-Type to advertise both JSON and SSE on\n // a POST. Real MCP clients (Claude Code, `agent-native connect`) always\n // send these; we never inject/alter them — if they're absent the SDK\n // returns its spec-mandated 406/415, identical to the Node path.\n\n const host = headers.get(\"host\") || \"localhost\";\n const forwardedProto = headers.get(\"x-forwarded-proto\");\n const proto =\n forwardedProto?.split(\",\")[0]?.trim() ||\n (/^(localhost|127\\.0\\.0\\.1)(:|$)/.test(host) ? \"http\" : \"https\");\n let url = `${proto}://${host}/_agent-native/mcp`;\n try {\n if (src?.url) url = new URL(src.url).href;\n } catch {\n // keep the synthesized URL\n }\n\n // No body here on purpose: the JSON-RPC payload is forwarded via the\n // transport's `parsedBody` option (the same mechanism the Node transport\n // uses), so the request stream is never read twice.\n return new Request(url, { method, headers });\n}\n\n// ---------------------------------------------------------------------------\n// handleMcpRequest — runtime-agnostic MCP request handler\n// ---------------------------------------------------------------------------\n\n/**\n * Handle a single `{routePrefix}/mcp` request on either runtime.\n *\n * - **Node fast-path** (real Node HTTP server): unchanged — delegate to the\n * SDK's `StreamableHTTPServerTransport.handleRequest(nodeReq, nodeRes,\n * body)`, which writes directly to the Node response (full protocol incl.\n * SSE).\n * - **Web-standard fallback** (Nitro 3 / Netlify web runtime, Cloudflare,\n * Deno, Bun — where there is no Node req/res): build the SAME MCP `Server`\n * from the SAME config + identity, drive it through the SDK's\n * `WebStandardStreamableHTTPServerTransport` (which the Node transport is\n * itself just a thin wrapper around), and return the resulting Web\n * `Response` as a normal h3 return value.\n *\n * Auth, the `runWithRequestContext` identity wrap, the deep-link `_meta` /\n * markdown append, `requestMeta` origin/target derivation and the stateless\n * semantics are IDENTICAL on both paths because both build the same server\n * via `createMCPServerForRequest` and both transports funnel into the same\n * `WebStandardStreamableHTTPServerTransport.handleRequest(webRequest, {\n * parsedBody })` with the same options.\n *\n * Returns:\n * - `undefined` when the request targets a sub-route (so management/status\n * routes mounted under `/_agent-native/mcp/*` handle it themselves) — the\n * h3 mount falls through to the next handler.\n * - a Web `Response` (web fallback) or a string/object (Node path /\n * auth-error path) otherwise. The Node path also sets `_handled` so h3\n * doesn't double-write.\n */\nexport async function handleMcpRequest(\n event: H3Event,\n config: MCPConfig,\n): Promise<Response | string | { error: string } | undefined> {\n const pathname = event.url?.pathname || \"/\";\n const subpath = pathname.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n if (subpath) {\n // Let management/status routes mounted under /_agent-native/mcp/* handle\n // their own requests instead of treating them as MCP protocol traffic.\n return undefined;\n }\n\n const method = getMethod(event);\n\n // Auth check — extracts the caller's identity from the JWT (`sub`), or, on\n // the static-token / dev-open path, from the forwarded\n // `X-Agent-Native-Owner-Email` hint the stdio proxy sends (the\n // `agent-native mcp install` flow). Without this the install flow would run\n // every tool unscoped (userEmail === undefined).\n const authHeader = getRequestHeader(event, \"authorization\");\n const ownerEmailHeader = getRequestHeader(\n event,\n \"x-agent-native-owner-email\",\n );\n const authResult = await verifyAuth(authHeader, ownerEmailHeader);\n if (!authResult.authed) {\n setResponseStatus(event, 401);\n return { error: \"Unauthorized\" };\n }\n\n // Stateless mode: only POST is meaningful\n if (method === \"DELETE\") {\n setResponseStatus(event, 204);\n return \"\";\n }\n\n if (method !== \"POST\" && method !== \"GET\") {\n setResponseStatus(event, 405);\n return { error: \"Method not allowed\" };\n }\n\n // Read body for POST (GET has no body). Read it via the h3 helper exactly\n // once; both transports accept it as a pre-parsed body so the request\n // stream is never consumed twice.\n const body = method === \"POST\" ? await readBody(event) : undefined;\n\n // Per-request stateless transport + server. Both runtimes build the SAME\n // server from the SAME config + verified identity + request meta, so\n // tools/list, tools/call, and the deep-link `_meta` are identical.\n const requestMeta = deriveRequestMeta(event);\n const server = await createMCPServerForRequest(\n config,\n authResult.identity,\n requestMeta,\n );\n\n const { nodeReq, nodeRes } = getNodeReqRes(event);\n\n if (nodeReq && nodeRes) {\n // ---- Node fast-path (UNCHANGED behavior) --------------------------------\n const { StreamableHTTPServerTransport } =\n await import(\"@modelcontextprotocol/sdk/server/streamableHttp.js\");\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined, // stateless\n });\n await server.connect(transport);\n try {\n // The SDK transport writes directly to the Node response. Node-only by\n // construction; we only reach here when real Node req/res exist.\n await transport.handleRequest(nodeReq, nodeRes, body);\n } catch (err: any) {\n // The SDK transport writes directly to the Node response. If the socket\n // is already closed/ended (client disconnected, or the host stream\n // layer also flushed), Node throws ERR_STREAM_WRITE_AFTER_END *after*\n // the MCP payload was already delivered correctly. Swallow that benign\n // post-flush write so an external agent disconnecting mid-stream can\n // never take down the server process; rethrow anything else.\n if (err?.code !== \"ERR_STREAM_WRITE_AFTER_END\") throw err;\n if (process.env.DEBUG)\n console.log(\n \"[mcp] ignored post-flush ERR_STREAM_WRITE_AFTER_END (client disconnected)\",\n );\n }\n // Prevent H3 from double-writing the response\n (event as any)._handled = true;\n return undefined;\n }\n\n // ---- Web-standard fallback (Nitro 3 / Netlify web runtime, CF, Deno,\n // Bun) ---------------------------------------------------------------------\n //\n // `StreamableHTTPServerTransport` is itself just a thin wrapper that\n // converts the Node req/res to a web Request/Response and delegates to\n // `WebStandardStreamableHTTPServerTransport.handleRequest(webRequest, {\n // parsedBody })`. Using the web transport directly with the SAME options +\n // the same pre-read `parsedBody` produces byte-identical protocol output\n // (including the deep-link `_meta` built inside createMCPServerForRequest),\n // and works on every web runtime because it returns a Web `Response`\n // (JSON for request/response, or an SSE `ReadableStream` body which h3\n // streams natively).\n const { WebStandardStreamableHTTPServerTransport } =\n await import(\"@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js\");\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined, // stateless — same as the Node path\n });\n await server.connect(transport);\n const webRequest = buildWebRequest(event, method);\n // `parsedBody: undefined` would make the SDK try to read `req.json()`; our\n // synthesized request has no body, so only pass the option for POST (where\n // we actually have a parsed body). For GET the transport reads no body.\n const response = await transport.handleRequest(\n webRequest,\n method === \"POST\" ? { parsedBody: body } : undefined,\n );\n return response;\n}\n\n// ---------------------------------------------------------------------------\n// mountMCP — register MCP Streamable HTTP endpoint on H3/Nitro\n// ---------------------------------------------------------------------------\n\n/**\n * Mount an MCP remote server on an H3/Nitro app.\n *\n * Endpoint: `{routePrefix}/mcp` (default `/_agent-native/mcp`)\n *\n * Uses stateless Streamable HTTP transport — no in-memory sessions,\n * compatible with serverless deployments. Runtime-agnostic: a real Node\n * server uses the SDK's Node transport; the web-standard runtime (Nitro 3 /\n * Netlify web runtime, Cloudflare, Deno, Bun) uses the SDK's web-standard\n * transport. Both build the same server and produce identical JSON-RPC\n * output.\n *\n * Auth: Bearer token matching ACCESS_TOKEN/ACCESS_TOKENS or JWT via A2A_SECRET.\n * No auth required when neither is configured (dev mode).\n */\nexport function mountMCP(\n nitroApp: any,\n config: MCPConfig,\n routePrefix = \"/_agent-native\",\n): void {\n getH3App(nitroApp).use(\n `${routePrefix}/mcp`,\n defineEventHandler(async (event) => {\n return handleMcpRequest(event as H3Event, config);\n }),\n );\n\n if (process.env.DEBUG)\n console.log(\n `[mcp] Mounted MCP server at ${routePrefix}/mcp (${Object.keys(config.actions).length} tools${config.askAgent ? \" + ask-agent\" : \"\"})`,\n );\n}\n"]}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dev-mode script registry.
|
|
3
3
|
*
|
|
4
|
-
* Provides
|
|
4
|
+
* Provides shared coding and database tools for the agent
|
|
5
5
|
* when running in development mode. These tools should NEVER be
|
|
6
6
|
* registered in production.
|
|
7
7
|
*/
|
|
8
8
|
import type { ActionEntry } from "../../agent/production-agent.js";
|
|
9
9
|
/**
|
|
10
|
-
* Creates the dev-mode script registry with
|
|
11
|
-
* and database tools. Call this and merge with your app's registry
|
|
10
|
+
* Creates the dev-mode script registry with shared bash/read/edit/write
|
|
11
|
+
* coding tools and database tools. Call this and merge with your app's registry
|
|
12
12
|
* when NODE_ENV !== "production".
|
|
13
13
|
*/
|
|
14
|
-
export declare function createDevScriptRegistry(
|
|
14
|
+
export declare function createDevScriptRegistry(options?: {
|
|
15
|
+
legacyAliases?: boolean;
|
|
16
|
+
}): Promise<Record<string, ActionEntry>>;
|
|
15
17
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/scripts/dev/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/scripts/dev/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAsDnE;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CA2MtC"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dev-mode script registry.
|
|
3
3
|
*
|
|
4
|
-
* Provides
|
|
4
|
+
* Provides shared coding and database tools for the agent
|
|
5
5
|
* when running in development mode. These tools should NEVER be
|
|
6
6
|
* registered in production.
|
|
7
7
|
*/
|
|
8
|
+
import { createCodingToolRegistry } from "../../coding-tools/index.js";
|
|
8
9
|
import { tool as readFileTool, run as readFileRun } from "./read-file.js";
|
|
9
10
|
import { tool as writeFileTool, run as writeFileRun } from "./write-file.js";
|
|
10
11
|
import { tool as listFilesTool, run as listFilesRun } from "./list-files.js";
|
|
@@ -47,11 +48,11 @@ function wrapCliScript(tool, cliDefault, opts) {
|
|
|
47
48
|
};
|
|
48
49
|
}
|
|
49
50
|
/**
|
|
50
|
-
* Creates the dev-mode script registry with
|
|
51
|
-
* and database tools. Call this and merge with your app's registry
|
|
51
|
+
* Creates the dev-mode script registry with shared bash/read/edit/write
|
|
52
|
+
* coding tools and database tools. Call this and merge with your app's registry
|
|
52
53
|
* when NODE_ENV !== "production".
|
|
53
54
|
*/
|
|
54
|
-
export async function createDevScriptRegistry() {
|
|
55
|
+
export async function createDevScriptRegistry(options = {}) {
|
|
55
56
|
// Lazy-import DB scripts to avoid requiring libsql in non-DB apps
|
|
56
57
|
let dbEntries = {};
|
|
57
58
|
try {
|
|
@@ -190,16 +191,30 @@ export async function createDevScriptRegistry() {
|
|
|
190
191
|
catch {
|
|
191
192
|
// DB scripts not available (no libsql) — skip silently
|
|
192
193
|
}
|
|
194
|
+
const codingEntries = createCodingToolRegistry({
|
|
195
|
+
cwd: process.cwd(),
|
|
196
|
+
bashThrowsOnNonZero: true,
|
|
197
|
+
});
|
|
198
|
+
const legacyEntries = options.legacyAliases
|
|
199
|
+
? {
|
|
200
|
+
"read-file": { tool: readFileTool, run: readFileRun, readOnly: true },
|
|
201
|
+
"write-file": { tool: writeFileTool, run: writeFileRun },
|
|
202
|
+
"list-files": {
|
|
203
|
+
tool: listFilesTool,
|
|
204
|
+
run: listFilesRun,
|
|
205
|
+
readOnly: true,
|
|
206
|
+
},
|
|
207
|
+
"search-files": {
|
|
208
|
+
tool: searchFilesTool,
|
|
209
|
+
run: searchFilesRun,
|
|
210
|
+
readOnly: true,
|
|
211
|
+
},
|
|
212
|
+
shell: { tool: shellTool, run: shellRun },
|
|
213
|
+
}
|
|
214
|
+
: {};
|
|
193
215
|
return {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
"list-files": { tool: listFilesTool, run: listFilesRun, readOnly: true },
|
|
197
|
-
"search-files": {
|
|
198
|
-
tool: searchFilesTool,
|
|
199
|
-
run: searchFilesRun,
|
|
200
|
-
readOnly: true,
|
|
201
|
-
},
|
|
202
|
-
shell: { tool: shellTool, run: shellRun },
|
|
216
|
+
...codingEntries,
|
|
217
|
+
...legacyEntries,
|
|
203
218
|
...dbEntries,
|
|
204
219
|
};
|
|
205
220
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/scripts/dev/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Dev-mode script registry.\n *\n * Provides file system, shell, and database tools for the agent\n * when running in development mode. These tools should NEVER be\n * registered in production.\n */\n\nimport type { ActionTool } from \"../../agent/types.js\";\nimport type { ActionEntry } from \"../../agent/production-agent.js\";\nimport { tool as readFileTool, run as readFileRun } from \"./read-file.js\";\nimport { tool as writeFileTool, run as writeFileRun } from \"./write-file.js\";\nimport { tool as listFilesTool, run as listFilesRun } from \"./list-files.js\";\nimport {\n tool as searchFilesTool,\n run as searchFilesRun,\n} from \"./search-files.js\";\nimport { tool as shellTool, run as shellRun } from \"./shell.js\";\n\n/**\n * Wraps a core CLI script (that writes to console.log) as a ActionEntry\n * by capturing stdout.\n */\nfunction wrapCliScript(\n tool: ActionTool,\n cliDefault: (args: string[]) => Promise<void>,\n opts?: { readOnly?: boolean },\n): ActionEntry {\n return {\n tool,\n ...(opts?.readOnly ? { readOnly: true as const } : {}),\n run: async (args: Record<string, string>): Promise<string> => {\n const cliArgs: string[] = [];\n for (const [k, v] of Object.entries(args)) {\n const raw = v as unknown;\n const value =\n raw != null && typeof raw === \"object\"\n ? JSON.stringify(raw)\n : String(raw);\n cliArgs.push(`--${k}`, value);\n }\n\n // Capture console.log output\n const logs: string[] = [];\n const origLog = console.log;\n console.log = (...a: unknown[]) => {\n logs.push(a.map(String).join(\" \"));\n };\n\n try {\n await cliDefault(cliArgs);\n } catch (err: any) {\n logs.push(`Error: ${err?.message ?? String(err)}`);\n } finally {\n console.log = origLog;\n }\n\n return logs.join(\"\\n\") || \"(no output)\";\n },\n };\n}\n\n/**\n * Creates the dev-mode script registry with file system, shell,\n * and database tools. Call this and merge with your app's registry\n * when NODE_ENV !== \"production\".\n */\nexport async function createDevScriptRegistry(): Promise<\n Record<string, ActionEntry>\n> {\n // Lazy-import DB scripts to avoid requiring libsql in non-DB apps\n let dbEntries: Record<string, ActionEntry> = {};\n try {\n // Dynamic imports — these are part of @agent-native/core\n const [dbSchema, dbQuery, dbExec, dbPatch, dbCheckScoping] =\n await Promise.all([\n import(\"../db/schema.js\"),\n import(\"../db/query.js\"),\n import(\"../db/exec.js\"),\n import(\"../db/patch.js\"),\n import(\"../db/check-scoping.js\"),\n ]);\n\n dbEntries = {\n \"db-schema\": wrapCliScript(\n {\n description:\n \"Show all database tables, columns, types, and foreign keys\",\n parameters: {\n type: \"object\",\n properties: {\n format: {\n type: \"string\",\n description: 'Output format: \"json\" or \"text\" (default: text)',\n enum: [\"json\", \"text\"],\n },\n },\n },\n },\n dbSchema.default,\n { readOnly: true },\n ),\n \"db-query\": wrapCliScript(\n {\n description:\n \"Run a read-only SQL query (SELECT, WITH, EXPLAIN, PRAGMA) against the app database\",\n parameters: {\n type: \"object\",\n properties: {\n sql: {\n type: \"string\",\n description: \"The SQL SELECT query to execute\",\n },\n args: {\n type: \"string\",\n description:\n 'Optional JSON array of positional bind args for parameterized placeholders. Example: \\'[\"draft\",\"form-123\"]\\'',\n },\n format: {\n type: \"string\",\n description:\n 'Output format: \"json\" or \"table\" (default: table)',\n enum: [\"json\", \"table\"],\n },\n },\n required: [\"sql\"],\n },\n },\n dbQuery.default,\n { readOnly: true },\n ),\n \"db-exec\": wrapCliScript(\n {\n description:\n \"Execute app-database write SQL (INSERT, UPDATE, DELETE, REPLACE). For multiple related writes, pass `statements` so they run sequentially in one transaction instead of issuing several db-exec calls. Schema changes (CREATE/ALTER/DROP) are blocked.\",\n parameters: {\n type: \"object\",\n properties: {\n sql: {\n type: \"string\",\n description:\n \"Single INSERT / UPDATE / DELETE / REPLACE statement. Use parameterized placeholders (?) where possible.\",\n },\n args: {\n type: \"string\",\n description:\n 'Optional JSON array of positional bind args for `sql`. Example: \\'[\"published\",\"form-123\"]\\'',\n },\n statements: {\n type: \"string\",\n description:\n 'Optional JSON array of write statements to execute in one transaction. Prefer this over multiple db-exec calls. Example: \\'[{\"sql\":\"INSERT INTO notes (id,title) VALUES (?,?)\",\"args\":[\"n1\",\"One\"]},{\"sql\":\"UPDATE counters SET value = value + 1 WHERE key = ?\",\"args\":[\"notes\"]}]\\'',\n },\n format: {\n type: \"string\",\n description: 'Output format: \"json\" or \"text\" (default: text)',\n enum: [\"json\", \"text\"],\n },\n },\n },\n },\n dbExec.default,\n ),\n \"db-patch\": wrapCliScript(\n {\n description:\n \"Surgical search-and-replace on a text column in a SQL table. Prefer over `db-exec UPDATE` for large text fields (documents, slides, dashboards, JSON blobs) where you only need to change a small slice — avoids re-sending the full column value. Targets exactly one row at a time (narrow --where by primary key). If a template-specific action exists for the table (e.g. `edit-document`, `update-slide`), use that instead — it will also push live updates to open collaborative editors.\",\n parameters: {\n type: \"object\",\n properties: {\n table: {\n type: \"string\",\n description: \"Target table name (plain identifier, no quoting)\",\n },\n column: {\n type: \"string\",\n description:\n \"Target text column name (plain identifier, no quoting)\",\n },\n where: {\n type: \"string\",\n description:\n \"SQL WHERE clause that matches exactly one row, e.g. \\\"id = 'abc123'\\\". Must not contain semicolons or DDL keywords.\",\n },\n find: {\n type: \"string\",\n description:\n \"Text to find (single-edit mode). Pair with --replace.\",\n },\n replace: {\n type: \"string\",\n description:\n 'Replacement text (single-edit mode). Defaults to \"\" (delete the match).',\n },\n edits: {\n type: \"string\",\n description:\n 'Batch mode: JSON array of {find, replace} objects. Example: \\'[{\"find\":\"Q3\",\"replace\":\"Q4\"},{\"find\":\"$1M\",\"replace\":\"$1.2M\"}]\\'',\n },\n all: {\n type: \"string\",\n description:\n 'Set to \"true\" to replace every occurrence of each find (default: first occurrence only).',\n enum: [\"true\", \"false\"],\n },\n format: {\n type: \"string\",\n description: 'Output format: \"json\" or \"text\" (default: text)',\n enum: [\"json\", \"text\"],\n },\n },\n required: [\"table\", \"column\", \"where\"],\n },\n },\n dbPatch.default,\n ),\n \"db-check-scoping\": wrapCliScript(\n {\n description:\n \"Validate that all template tables have owner_email and org_id columns for data scoping\",\n parameters: {\n type: \"object\",\n properties: {\n \"require-org\": {\n type: \"string\",\n description:\n 'Set to \"true\" to also require org_id columns (for multi-org apps)',\n enum: [\"true\", \"false\"],\n },\n format: {\n type: \"string\",\n description: 'Output format: \"json\" or \"text\" (default: text)',\n enum: [\"json\", \"text\"],\n },\n },\n },\n },\n dbCheckScoping.default,\n { readOnly: true },\n ),\n };\n } catch {\n // DB scripts not available (no libsql) — skip silently\n }\n\n return {\n \"read-file\": { tool: readFileTool, run: readFileRun, readOnly: true },\n \"write-file\": { tool: writeFileTool, run: writeFileRun },\n \"list-files\": { tool: listFilesTool, run: listFilesRun, readOnly: true },\n \"search-files\": {\n tool: searchFilesTool,\n run: searchFilesRun,\n readOnly: true,\n },\n shell: { tool: shellTool, run: shellRun },\n ...dbEntries,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/scripts/dev/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Dev-mode script registry.\n *\n * Provides shared coding and database tools for the agent\n * when running in development mode. These tools should NEVER be\n * registered in production.\n */\n\nimport type { ActionTool } from \"../../agent/types.js\";\nimport type { ActionEntry } from \"../../agent/production-agent.js\";\nimport { createCodingToolRegistry } from \"../../coding-tools/index.js\";\nimport { tool as readFileTool, run as readFileRun } from \"./read-file.js\";\nimport { tool as writeFileTool, run as writeFileRun } from \"./write-file.js\";\nimport { tool as listFilesTool, run as listFilesRun } from \"./list-files.js\";\nimport {\n tool as searchFilesTool,\n run as searchFilesRun,\n} from \"./search-files.js\";\nimport { tool as shellTool, run as shellRun } from \"./shell.js\";\n\n/**\n * Wraps a core CLI script (that writes to console.log) as a ActionEntry\n * by capturing stdout.\n */\nfunction wrapCliScript(\n tool: ActionTool,\n cliDefault: (args: string[]) => Promise<void>,\n opts?: { readOnly?: boolean },\n): ActionEntry {\n return {\n tool,\n ...(opts?.readOnly ? { readOnly: true as const } : {}),\n run: async (args: Record<string, string>): Promise<string> => {\n const cliArgs: string[] = [];\n for (const [k, v] of Object.entries(args)) {\n const raw = v as unknown;\n const value =\n raw != null && typeof raw === \"object\"\n ? JSON.stringify(raw)\n : String(raw);\n cliArgs.push(`--${k}`, value);\n }\n\n // Capture console.log output\n const logs: string[] = [];\n const origLog = console.log;\n console.log = (...a: unknown[]) => {\n logs.push(a.map(String).join(\" \"));\n };\n\n try {\n await cliDefault(cliArgs);\n } catch (err: any) {\n logs.push(`Error: ${err?.message ?? String(err)}`);\n } finally {\n console.log = origLog;\n }\n\n return logs.join(\"\\n\") || \"(no output)\";\n },\n };\n}\n\n/**\n * Creates the dev-mode script registry with shared bash/read/edit/write\n * coding tools and database tools. Call this and merge with your app's registry\n * when NODE_ENV !== \"production\".\n */\nexport async function createDevScriptRegistry(\n options: { legacyAliases?: boolean } = {},\n): Promise<Record<string, ActionEntry>> {\n // Lazy-import DB scripts to avoid requiring libsql in non-DB apps\n let dbEntries: Record<string, ActionEntry> = {};\n try {\n // Dynamic imports — these are part of @agent-native/core\n const [dbSchema, dbQuery, dbExec, dbPatch, dbCheckScoping] =\n await Promise.all([\n import(\"../db/schema.js\"),\n import(\"../db/query.js\"),\n import(\"../db/exec.js\"),\n import(\"../db/patch.js\"),\n import(\"../db/check-scoping.js\"),\n ]);\n\n dbEntries = {\n \"db-schema\": wrapCliScript(\n {\n description:\n \"Show all database tables, columns, types, and foreign keys\",\n parameters: {\n type: \"object\",\n properties: {\n format: {\n type: \"string\",\n description: 'Output format: \"json\" or \"text\" (default: text)',\n enum: [\"json\", \"text\"],\n },\n },\n },\n },\n dbSchema.default,\n { readOnly: true },\n ),\n \"db-query\": wrapCliScript(\n {\n description:\n \"Run a read-only SQL query (SELECT, WITH, EXPLAIN, PRAGMA) against the app database\",\n parameters: {\n type: \"object\",\n properties: {\n sql: {\n type: \"string\",\n description: \"The SQL SELECT query to execute\",\n },\n args: {\n type: \"string\",\n description:\n 'Optional JSON array of positional bind args for parameterized placeholders. Example: \\'[\"draft\",\"form-123\"]\\'',\n },\n format: {\n type: \"string\",\n description:\n 'Output format: \"json\" or \"table\" (default: table)',\n enum: [\"json\", \"table\"],\n },\n },\n required: [\"sql\"],\n },\n },\n dbQuery.default,\n { readOnly: true },\n ),\n \"db-exec\": wrapCliScript(\n {\n description:\n \"Execute app-database write SQL (INSERT, UPDATE, DELETE, REPLACE). For multiple related writes, pass `statements` so they run sequentially in one transaction instead of issuing several db-exec calls. Schema changes (CREATE/ALTER/DROP) are blocked.\",\n parameters: {\n type: \"object\",\n properties: {\n sql: {\n type: \"string\",\n description:\n \"Single INSERT / UPDATE / DELETE / REPLACE statement. Use parameterized placeholders (?) where possible.\",\n },\n args: {\n type: \"string\",\n description:\n 'Optional JSON array of positional bind args for `sql`. Example: \\'[\"published\",\"form-123\"]\\'',\n },\n statements: {\n type: \"string\",\n description:\n 'Optional JSON array of write statements to execute in one transaction. Prefer this over multiple db-exec calls. Example: \\'[{\"sql\":\"INSERT INTO notes (id,title) VALUES (?,?)\",\"args\":[\"n1\",\"One\"]},{\"sql\":\"UPDATE counters SET value = value + 1 WHERE key = ?\",\"args\":[\"notes\"]}]\\'',\n },\n format: {\n type: \"string\",\n description: 'Output format: \"json\" or \"text\" (default: text)',\n enum: [\"json\", \"text\"],\n },\n },\n },\n },\n dbExec.default,\n ),\n \"db-patch\": wrapCliScript(\n {\n description:\n \"Surgical search-and-replace on a text column in a SQL table. Prefer over `db-exec UPDATE` for large text fields (documents, slides, dashboards, JSON blobs) where you only need to change a small slice — avoids re-sending the full column value. Targets exactly one row at a time (narrow --where by primary key). If a template-specific action exists for the table (e.g. `edit-document`, `update-slide`), use that instead — it will also push live updates to open collaborative editors.\",\n parameters: {\n type: \"object\",\n properties: {\n table: {\n type: \"string\",\n description: \"Target table name (plain identifier, no quoting)\",\n },\n column: {\n type: \"string\",\n description:\n \"Target text column name (plain identifier, no quoting)\",\n },\n where: {\n type: \"string\",\n description:\n \"SQL WHERE clause that matches exactly one row, e.g. \\\"id = 'abc123'\\\". Must not contain semicolons or DDL keywords.\",\n },\n find: {\n type: \"string\",\n description:\n \"Text to find (single-edit mode). Pair with --replace.\",\n },\n replace: {\n type: \"string\",\n description:\n 'Replacement text (single-edit mode). Defaults to \"\" (delete the match).',\n },\n edits: {\n type: \"string\",\n description:\n 'Batch mode: JSON array of {find, replace} objects. Example: \\'[{\"find\":\"Q3\",\"replace\":\"Q4\"},{\"find\":\"$1M\",\"replace\":\"$1.2M\"}]\\'',\n },\n all: {\n type: \"string\",\n description:\n 'Set to \"true\" to replace every occurrence of each find (default: first occurrence only).',\n enum: [\"true\", \"false\"],\n },\n format: {\n type: \"string\",\n description: 'Output format: \"json\" or \"text\" (default: text)',\n enum: [\"json\", \"text\"],\n },\n },\n required: [\"table\", \"column\", \"where\"],\n },\n },\n dbPatch.default,\n ),\n \"db-check-scoping\": wrapCliScript(\n {\n description:\n \"Validate that all template tables have owner_email and org_id columns for data scoping\",\n parameters: {\n type: \"object\",\n properties: {\n \"require-org\": {\n type: \"string\",\n description:\n 'Set to \"true\" to also require org_id columns (for multi-org apps)',\n enum: [\"true\", \"false\"],\n },\n format: {\n type: \"string\",\n description: 'Output format: \"json\" or \"text\" (default: text)',\n enum: [\"json\", \"text\"],\n },\n },\n },\n },\n dbCheckScoping.default,\n { readOnly: true },\n ),\n };\n } catch {\n // DB scripts not available (no libsql) — skip silently\n }\n\n const codingEntries = createCodingToolRegistry({\n cwd: process.cwd(),\n bashThrowsOnNonZero: true,\n });\n const legacyEntries: Record<string, ActionEntry> = options.legacyAliases\n ? {\n \"read-file\": { tool: readFileTool, run: readFileRun, readOnly: true },\n \"write-file\": { tool: writeFileTool, run: writeFileRun },\n \"list-files\": {\n tool: listFilesTool,\n run: listFilesRun,\n readOnly: true,\n },\n \"search-files\": {\n tool: searchFilesTool,\n run: searchFilesRun,\n readOnly: true,\n },\n shell: { tool: shellTool, run: shellRun },\n }\n : {};\n\n return {\n ...codingEntries,\n ...legacyEntries,\n ...dbEntries,\n };\n}\n"]}
|
|
@@ -128,10 +128,10 @@ export interface AgentChatPluginOptions {
|
|
|
128
128
|
*
|
|
129
129
|
* When set, the same lean prompt is used in both dev and prod modes. In
|
|
130
130
|
* dev mode the tool registry is ALSO swapped to the template's actions
|
|
131
|
-
* (same set as prod) — the dev-only
|
|
131
|
+
* (same set as prod) — the dev-only bash/db-exec/file-system tools
|
|
132
132
|
* and the resource/docs/chat/team/job/browser scripts are dropped. The
|
|
133
|
-
* lean system prompt has no
|
|
134
|
-
* through
|
|
133
|
+
* lean system prompt has no bash-usage guidance, so routing actions
|
|
134
|
+
* through bash would break. If you need the full dev tool surface,
|
|
135
135
|
* leave this off.
|
|
136
136
|
*/
|
|
137
137
|
leanPrompt?: boolean;
|
|
@@ -153,7 +153,7 @@ export interface AgentChatPluginOptions {
|
|
|
153
153
|
/**
|
|
154
154
|
* In dev mode, register the template's actions as native tools the agent
|
|
155
155
|
* can call directly with structured JSON args — skipping the default
|
|
156
|
-
* `
|
|
156
|
+
* `bash(command="pnpm action <name> ...")` indirection.
|
|
157
157
|
*
|
|
158
158
|
* The default dev behavior shells out because it "mirrors how Claude Code
|
|
159
159
|
* works locally" and reduces empty-object tool calls for templates with
|
|
@@ -163,7 +163,7 @@ export interface AgentChatPluginOptions {
|
|
|
163
163
|
* on the way out.
|
|
164
164
|
*
|
|
165
165
|
* Set to `true` to get the same tool surface in dev that production uses.
|
|
166
|
-
* `leanPrompt: true` implies this already (lean mode has no
|
|
166
|
+
* `leanPrompt: true` implies this already (lean mode has no bash-usage
|
|
167
167
|
* guidance, so actions must be native). Set this flag without
|
|
168
168
|
* `leanPrompt` when you want native actions AND the full system prompt.
|
|
169
169
|
*
|
|
@@ -210,7 +210,7 @@ export declare function loadResourcesForPrompt(owner: string, compact?: boolean,
|
|
|
210
210
|
export declare function createAgentChatPlugin(options?: AgentChatPluginOptions): NitroPluginDef;
|
|
211
211
|
/**
|
|
212
212
|
* Default agent chat plugin with no template-specific actions.
|
|
213
|
-
* In dev mode, provides file system,
|
|
213
|
+
* In dev mode, provides file system, bash, and database tools.
|
|
214
214
|
* In production, provides only the default system prompt.
|
|
215
215
|
*/
|
|
216
216
|
export declare const defaultAgentChatPlugin: NitroPluginDef;
|