@cursor/july 0.1.68 → 0.1.73
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/AGENTS.md +4 -5
- package/README.md +23 -11
- package/dist/bin/agent-serve.js +3 -2
- package/dist/channels/github/api.d.ts +2 -0
- package/dist/channels/github/api.d.ts.map +1 -1
- package/dist/channels/github/api.js +2 -1
- package/dist/channels/github/binding.d.ts +7 -0
- package/dist/channels/github/binding.d.ts.map +1 -1
- package/dist/channels/github/binding.js +4 -0
- package/dist/channels/github/checks.d.ts +99 -0
- package/dist/channels/github/checks.d.ts.map +1 -0
- package/dist/channels/github/checks.js +108 -0
- package/dist/channels/github/cursor-account.d.ts +4 -4
- package/dist/channels/github/cursor-account.d.ts.map +1 -1
- package/dist/channels/github/cursor-account.js +7 -5
- package/dist/channels/github/index.d.ts +1 -0
- package/dist/channels/github/index.d.ts.map +1 -1
- package/dist/channels/github/index.js +1 -0
- package/dist/channels/github/types.d.ts +8 -0
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/slack/manifest.d.ts +2 -2
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +13 -11
- package/dist/channels/slack/types.d.ts +2 -2
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/connections.d.ts +17 -5
- package/dist/connections.d.ts.map +1 -1
- package/dist/connections.js +17 -5
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.C5XUTCw9.js → app.CdbcFD8T.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DQad--_z.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.CGDTAYHY.js → VPLocalSearchBox.D_NKImM7.js} +1 -1
- package/dist/docs/assets/chunks/{theme.lPixh250.js → theme.CMvz7cu9.js} +2 -2
- package/dist/docs/assets/{concepts.md.CqOsxbMU.js → concepts.md.BXAm6G-C.js} +1 -1
- package/dist/docs/assets/{guides_mcp-oauth.md.C7G7IykG.js → guides_mcp-oauth.md.CzEB6RaG.js} +1 -1
- package/dist/docs/assets/{reference_agent-config.md.DHkkvcEZ.js → reference_agent-config.md.BZnEKfEc.js} +3 -4
- package/dist/docs/assets/{reference_agent-config.md.DHkkvcEZ.lean.js → reference_agent-config.md.BZnEKfEc.lean.js} +1 -1
- package/dist/docs/assets/{reference_connections.md.Bwswbm6H.js → reference_connections.md.Cu3N-S3Q.js} +18 -11
- package/dist/docs/assets/{reference_connections.md.Bwswbm6H.lean.js → reference_connections.md.Cu3N-S3Q.lean.js} +1 -1
- package/dist/docs/assets/{reference_sessions.md.DBVFi2Sx.js → reference_sessions.md.B0DdlM-K.js} +1 -1
- package/dist/docs/assets/{reference_tools.md.Wmvb7Lob.js → reference_tools.md.DTg_kEsx.js} +1 -1
- package/dist/docs/assets/troubleshooting.md.DQ-hYuZT.js +1 -0
- package/dist/docs/building-with-agents.html +3 -3
- package/dist/docs/concepts.html +5 -5
- package/dist/docs/deployment.html +3 -3
- package/dist/docs/evals.html +3 -3
- package/dist/docs/example-agents/approval-buddy.html +3 -3
- package/dist/docs/example-agents/benny.html +3 -3
- package/dist/docs/example-agents/bugbot.html +3 -3
- package/dist/docs/example-agents/codebase-wiki.html +3 -3
- package/dist/docs/example-agents/codeowners-review.html +3 -3
- package/dist/docs/example-agents/concierge.html +3 -3
- package/dist/docs/example-agents/fsd.html +3 -3
- package/dist/docs/example-agents/index.html +3 -3
- package/dist/docs/example-agents/knowledge-base.html +3 -3
- package/dist/docs/example-agents/oncall.html +3 -3
- package/dist/docs/example-agents/security-reviewer.html +3 -3
- package/dist/docs/example-agents/slack-agent.html +3 -3
- package/dist/docs/example-agents/weather-agent.html +3 -3
- package/dist/docs/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.html +3 -3
- package/dist/docs/guides/convert-automation.html +3 -3
- package/dist/docs/guides/github.html +3 -3
- package/dist/docs/guides/human-in-the-loop.html +3 -3
- package/dist/docs/guides/mcp-oauth.html +5 -5
- package/dist/docs/guides/opentelemetry.html +3 -3
- package/dist/docs/guides/slack.html +3 -3
- package/dist/docs/guides/webhooks.html +3 -3
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +3 -3
- package/dist/docs/index.html +3 -3
- package/dist/docs/quickstart.html +3 -3
- package/dist/docs/reference/agent-config.html +5 -6
- package/dist/docs/reference/artifacts.html +3 -3
- package/dist/docs/reference/channels.html +3 -3
- package/dist/docs/reference/cli.html +3 -3
- package/dist/docs/reference/connections.html +20 -13
- package/dist/docs/reference/hooks.html +3 -3
- package/dist/docs/reference/http-api.html +3 -3
- package/dist/docs/reference/instructions.html +3 -3
- package/dist/docs/reference/playground.html +3 -3
- package/dist/docs/reference/project-layout.html +3 -3
- package/dist/docs/reference/prompt.html +3 -3
- package/dist/docs/reference/schedules.html +3 -3
- package/dist/docs/reference/sessions.html +5 -5
- package/dist/docs/reference/skills.html +3 -3
- package/dist/docs/reference/subagents.html +3 -3
- package/dist/docs/reference/tools.html +5 -5
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +3 -3
- package/dist/docs/templates/agentic-owners.html +3 -3
- package/dist/docs/templates/demo.html +3 -3
- package/dist/docs/templates/pr-autofixer.html +3 -3
- package/dist/docs/templates/security-reviewer.html +3 -3
- package/dist/docs/templates/triage.html +3 -3
- package/dist/docs/troubleshooting.html +5 -5
- package/dist/internal/advertise-tools.d.ts +11 -6
- package/dist/internal/advertise-tools.d.ts.map +1 -1
- package/dist/internal/advertise-tools.js +63 -30
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +2 -4
- package/dist/internal/convert-automation/convert-workflow.d.ts.map +1 -1
- package/dist/internal/convert-automation/convert-workflow.js +2 -10
- package/dist/internal/cursor/account-mcp.d.ts +29 -3
- package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
- package/dist/internal/cursor/account-mcp.js +147 -67
- package/dist/internal/cursor/backend-client.d.ts +11 -6
- package/dist/internal/cursor/backend-client.d.ts.map +1 -1
- package/dist/internal/cursor/backend-client.js +32 -10
- package/dist/internal/cursor-account-mcp-auth.d.ts +11 -0
- package/dist/internal/cursor-account-mcp-auth.d.ts.map +1 -1
- package/dist/internal/cursor-account-mcp-auth.js +69 -0
- package/dist/internal/deploy-client.d.ts +0 -7
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +2 -4
- package/dist/internal/deploy-manifest.d.ts +15 -24
- package/dist/internal/deploy-manifest.d.ts.map +1 -1
- package/dist/internal/deploy-manifest.js +38 -27
- package/dist/internal/deployment-scm-event-stream.d.ts +4 -1
- package/dist/internal/deployment-scm-event-stream.d.ts.map +1 -1
- package/dist/internal/deployment-scm-event-stream.js +15 -1
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +42 -10
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +7 -106
- package/dist/internal/init-scaffold.d.ts +8 -0
- package/dist/internal/init-scaffold.d.ts.map +1 -0
- package/dist/internal/init-scaffold.js +168 -0
- package/dist/internal/inline-stdio-mcp.d.ts +2 -0
- package/dist/internal/inline-stdio-mcp.d.ts.map +1 -1
- package/dist/internal/inline-stdio-mcp.js +10 -8
- package/dist/internal/json-schema.d.ts +7 -0
- package/dist/internal/json-schema.d.ts.map +1 -1
- package/dist/internal/json-schema.js +18 -3
- package/dist/internal/mcp-endpoint.js +7 -7
- package/dist/internal/mcp-host.js +4 -2
- package/dist/internal/mcp-oauth.d.ts +22 -0
- package/dist/internal/mcp-oauth.d.ts.map +1 -1
- package/dist/internal/mcp-oauth.js +40 -1
- package/dist/internal/package-inline-stdio.d.ts.map +1 -1
- package/dist/internal/package-inline-stdio.js +5 -5
- package/dist/internal/review-comments.d.ts +97 -0
- package/dist/internal/review-comments.d.ts.map +1 -1
- package/dist/internal/review-comments.js +151 -0
- package/dist/internal/sanitize-mcp-tool-name.d.ts +20 -0
- package/dist/internal/sanitize-mcp-tool-name.d.ts.map +1 -0
- package/dist/internal/sanitize-mcp-tool-name.js +30 -0
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +4 -6
- package/dist/internal/shallow-clone.d.ts +94 -39
- package/dist/internal/shallow-clone.d.ts.map +1 -1
- package/dist/internal/shallow-clone.js +188 -68
- package/dist/internal/terminal-style.d.ts +18 -0
- package/dist/internal/terminal-style.d.ts.map +1 -1
- package/dist/internal/terminal-style.js +46 -1
- package/dist/internal/workspace.d.ts +2 -0
- package/dist/internal/workspace.d.ts.map +1 -1
- package/dist/internal/workspace.js +5 -0
- package/dist/playground/assets/index-77uhu5Kr.css +1 -0
- package/dist/playground/assets/{index-yxs1heea.js → index-D4buox4m.js} +42 -42
- package/dist/playground/index.html +2 -2
- package/dist/tools.d.ts +7 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/types.d.ts +28 -3
- package/dist/types.d.ts.map +1 -1
- package/docs/concepts.md +4 -4
- package/docs/guides/mcp-oauth.md +3 -1
- package/docs/reference/agent-config.md +11 -12
- package/docs/reference/connections.md +44 -26
- package/docs/reference/sessions.md +2 -2
- package/docs/reference/tools.md +1 -1
- package/docs/troubleshooting.md +3 -2
- package/package.json +4 -1
- package/skills/create-agent/SKILL.md +12 -9
- package/skills/debug/SKILL.md +2 -1
- package/skills/framework-map/SKILL.md +6 -6
- package/skills/mcp-auth/SKILL.md +8 -6
- package/src/bin/agent-serve.ts +3 -2
- package/src/channels/github/api.ts +6 -1
- package/src/channels/github/binding.ts +21 -0
- package/src/channels/github/checks.ts +179 -0
- package/src/channels/github/cursor-account.ts +9 -6
- package/src/channels/github/index.ts +1 -0
- package/src/channels/github/types.ts +8 -0
- package/src/channels/slack/manifest.ts +8 -10
- package/src/channels/slack/types.ts +2 -2
- package/src/connections.ts +17 -5
- package/src/internal/advertise-tools.ts +72 -23
- package/src/internal/cli-deploy.ts +4 -15
- package/src/internal/convert-automation/convert-workflow.ts +2 -11
- package/src/internal/cursor/account-mcp.ts +208 -73
- package/src/internal/cursor/backend-client.ts +45 -11
- package/src/internal/cursor-account-mcp-auth.ts +83 -0
- package/src/internal/deploy-client.ts +0 -10
- package/src/internal/deploy-manifest.ts +33 -39
- package/src/internal/deployment-scm-event-stream.ts +17 -1
- package/src/internal/discovery.ts +47 -3
- package/src/internal/init-project.ts +7 -133
- package/src/internal/init-scaffold.ts +176 -0
- package/src/internal/inline-stdio-mcp.ts +13 -0
- package/src/internal/json-schema.ts +24 -3
- package/src/internal/mcp-endpoint.ts +6 -0
- package/src/internal/mcp-host.ts +4 -0
- package/src/internal/mcp-oauth.ts +55 -1
- package/src/internal/package-inline-stdio.ts +5 -1
- package/src/internal/review-comments.ts +245 -0
- package/src/internal/sanitize-mcp-tool-name.ts +33 -0
- package/src/internal/session-engine.ts +7 -0
- package/src/internal/shallow-clone.ts +293 -105
- package/src/internal/terminal-style.ts +67 -1
- package/src/internal/workspace.ts +6 -0
- package/src/tools.ts +7 -0
- package/src/types.ts +32 -4
- package/templates/agentic-owners/agent/agent.ts +3 -0
- package/templates/demo/agent/agent.ts +3 -0
- package/templates/security-reviewer/agent/agent.ts +3 -0
- package/templates/triage/agent/agent.ts +3 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.DvUR3lv9.js +0 -1
- package/dist/docs/assets/troubleshooting.md.CuVztCKs.js +0 -1
- package/dist/playground/assets/index-CoL7xCJl.css +0 -1
- /package/dist/docs/assets/{concepts.md.CqOsxbMU.lean.js → concepts.md.BXAm6G-C.lean.js} +0 -0
- /package/dist/docs/assets/{guides_mcp-oauth.md.C7G7IykG.lean.js → guides_mcp-oauth.md.CzEB6RaG.lean.js} +0 -0
- /package/dist/docs/assets/{reference_sessions.md.DBVFi2Sx.lean.js → reference_sessions.md.B0DdlM-K.lean.js} +0 -0
- /package/dist/docs/assets/{reference_tools.md.Wmvb7Lob.lean.js → reference_tools.md.DTg_kEsx.lean.js} +0 -0
- /package/dist/docs/assets/{troubleshooting.md.CuVztCKs.lean.js → troubleshooting.md.DQ-hYuZT.lean.js} +0 -0
|
@@ -16,9 +16,13 @@
|
|
|
16
16
|
* auth-less connections go through the shared `host.mcp` registry, which
|
|
17
17
|
* also covers serve-resolved transports (Cursor account bridges, peers).
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* Advertised names follow the Cursor-account MCP bridge
|
|
20
|
+
* (`packages/agent-serve/src/internal/cursor/account-mcp.ts`): slug
|
|
21
|
+
* `[^a-zA-Z0-9_-]` to `_`, collision suffixes `_2` / `_3`, and call the
|
|
22
|
+
* original advertised name. That is the same mapping `packages/agent`
|
|
23
|
+
* `normalizeMcpToolName` uses for IDE/Sand MCP tools. A listing failure
|
|
24
|
+
* still fails the turn rather than running it with a silently shrunken
|
|
25
|
+
* toolset.
|
|
22
26
|
*/
|
|
23
27
|
|
|
24
28
|
import type {
|
|
@@ -33,6 +37,10 @@ import type {
|
|
|
33
37
|
} from "../types.js";
|
|
34
38
|
import { brandDefinition } from "./brand.js";
|
|
35
39
|
import type { HostMcpOneOff } from "./mcp-host.js";
|
|
40
|
+
import {
|
|
41
|
+
sanitizeMcpToolName,
|
|
42
|
+
suffixMcpToolName,
|
|
43
|
+
} from "./sanitize-mcp-tool-name.js";
|
|
36
44
|
|
|
37
45
|
/**
|
|
38
46
|
* What the resolver needs from the host MCP registry: the shared cached
|
|
@@ -50,12 +58,12 @@ export interface AdvertiseHostMcp {
|
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
61
|
+
* Model-facing advertised names after {@link sanitizeMcpToolName} — the
|
|
62
|
+
* same MCP-client shape the Cursor-account bridge documents
|
|
63
|
+
* (`^[a-zA-Z0-9_-]{1,128}$`). Authored `agent/tools/` filenames stay on
|
|
64
|
+
* discovery's stricter `NAME_PATTERN` (must start alphanumeric).
|
|
56
65
|
*/
|
|
57
|
-
export const ADVERTISED_TOOL_NAME_PATTERN: RegExp =
|
|
58
|
-
/^[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
|
66
|
+
export const ADVERTISED_TOOL_NAME_PATTERN: RegExp = /^[A-Za-z0-9_-]{1,128}$/;
|
|
59
67
|
|
|
60
68
|
const ANY_OBJECT_SCHEMA: JsonSchemaObject = {
|
|
61
69
|
type: "object",
|
|
@@ -107,9 +115,10 @@ export async function resolveAdvertisedTools(args: {
|
|
|
107
115
|
{ cause: error }
|
|
108
116
|
);
|
|
109
117
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
118
|
+
const exposed = exposeListedTools(listed, claimed);
|
|
119
|
+
for (const { info, exposedName } of exposed) {
|
|
120
|
+
resolved.push(toDiscoveredTool(connection, info, exposedName, source));
|
|
121
|
+
claimed.add(exposedName);
|
|
113
122
|
}
|
|
114
123
|
}
|
|
115
124
|
return resolved;
|
|
@@ -154,30 +163,69 @@ export async function sourceForConnection(
|
|
|
154
163
|
});
|
|
155
164
|
}
|
|
156
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Same catalog rule as the Cursor-account bridge: slug illegal characters,
|
|
168
|
+
* then `_2` / `_3` … for colliders (including authored tools already in
|
|
169
|
+
* `claimed`). Suffix assignment is stable for a colliding group: sort by
|
|
170
|
+
* original advertised name, independent of `listTools` order.
|
|
171
|
+
*/
|
|
172
|
+
function exposeListedTools(
|
|
173
|
+
listed: HostMcpToolInfo[],
|
|
174
|
+
claimed: ReadonlySet<string>
|
|
175
|
+
): Array<{ info: HostMcpToolInfo; exposedName: string }> {
|
|
176
|
+
const byBase = new Map<string, HostMcpToolInfo[]>();
|
|
177
|
+
for (const info of listed) {
|
|
178
|
+
const base = sanitizeMcpToolName(info.name);
|
|
179
|
+
const group = byBase.get(base);
|
|
180
|
+
if (group === undefined) {
|
|
181
|
+
byBase.set(base, [info]);
|
|
182
|
+
} else {
|
|
183
|
+
group.push(info);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const taken = new Set(claimed);
|
|
187
|
+
const exposedNameByInfo = new Map<HostMcpToolInfo, string>();
|
|
188
|
+
const sortedBases = [...byBase.keys()].sort((left, right) =>
|
|
189
|
+
left < right ? -1 : left > right ? 1 : 0
|
|
190
|
+
);
|
|
191
|
+
for (const base of sortedBases) {
|
|
192
|
+
const group = byBase.get(base)!;
|
|
193
|
+
group.sort((left, right) =>
|
|
194
|
+
left.name < right.name ? -1 : left.name > right.name ? 1 : 0
|
|
195
|
+
);
|
|
196
|
+
group.forEach((info, index) => {
|
|
197
|
+
let suffixIndex = index;
|
|
198
|
+
let exposedName = suffixMcpToolName(base, suffixIndex);
|
|
199
|
+
while (taken.has(exposedName)) {
|
|
200
|
+
suffixIndex++;
|
|
201
|
+
exposedName = suffixMcpToolName(base, suffixIndex);
|
|
202
|
+
}
|
|
203
|
+
taken.add(exposedName);
|
|
204
|
+
exposedNameByInfo.set(info, exposedName);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return listed.map((info) => ({
|
|
208
|
+
info,
|
|
209
|
+
exposedName: exposedNameByInfo.get(info)!,
|
|
210
|
+
}));
|
|
211
|
+
}
|
|
212
|
+
|
|
157
213
|
function toDiscoveredTool(
|
|
158
214
|
connection: DiscoveredConnection,
|
|
159
215
|
info: HostMcpToolInfo,
|
|
160
|
-
|
|
216
|
+
exposedName: string,
|
|
161
217
|
source: AdvertisedToolSource
|
|
162
218
|
): DiscoveredTool {
|
|
163
|
-
if (!ADVERTISED_TOOL_NAME_PATTERN.test(info.name)) {
|
|
164
|
-
throw new Error(
|
|
165
|
-
`Advertised connection "${connection.name}" serves tool name "${info.name}", which must match ${ADVERTISED_TOOL_NAME_PATTERN} (it becomes the tool name the model sees).`
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
if (claimed.has(info.name)) {
|
|
169
|
-
throw new Error(
|
|
170
|
-
`Advertised connection "${connection.name}" serves tool "${info.name}", which collides with an authored agent/tools/ tool or another advertised connection.`
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
219
|
const description =
|
|
174
220
|
info.description === undefined || info.description.trim() === ""
|
|
175
221
|
? `${info.name} — MCP tool on the "${connection.name}" connection, forwarded 1:1.`
|
|
176
222
|
: info.description;
|
|
177
223
|
const inputSchema = info.inputSchema ?? ANY_OBJECT_SCHEMA;
|
|
224
|
+
const outputSchema = info.outputSchema;
|
|
178
225
|
const definition: ToolDefinition = brandDefinition("tool", {
|
|
179
226
|
description,
|
|
180
227
|
inputSchema,
|
|
228
|
+
...(outputSchema === undefined ? {} : { outputSchema }),
|
|
181
229
|
execute: async (input: Record<string, unknown>) =>
|
|
182
230
|
formatMcpToolResult(
|
|
183
231
|
connection.name,
|
|
@@ -186,9 +234,10 @@ function toDiscoveredTool(
|
|
|
186
234
|
),
|
|
187
235
|
} as unknown as ToolDefinition);
|
|
188
236
|
return {
|
|
189
|
-
name:
|
|
237
|
+
name: exposedName,
|
|
190
238
|
description,
|
|
191
239
|
inputSchema,
|
|
240
|
+
...(outputSchema === undefined ? {} : { outputSchema }),
|
|
192
241
|
execution: "server",
|
|
193
242
|
needsApproval: false,
|
|
194
243
|
definition,
|
|
@@ -39,10 +39,6 @@ import {
|
|
|
39
39
|
TERMINAL_DEPLOY_STATUSES,
|
|
40
40
|
validateDeploymentSlug,
|
|
41
41
|
} from "./deploy-client.js";
|
|
42
|
-
import {
|
|
43
|
-
type AgentDeployManifestV1,
|
|
44
|
-
buildAgentDeployManifest,
|
|
45
|
-
} from "./deploy-manifest.js";
|
|
46
42
|
import { type GitRunner, inferDeployGitSource } from "./deploy-source.js";
|
|
47
43
|
import {
|
|
48
44
|
type ChildAgentDir,
|
|
@@ -666,9 +662,10 @@ async function deployOne(
|
|
|
666
662
|
...(egressAllowedDomains.length === 0 ? {} : { egressAllowedDomains }),
|
|
667
663
|
publicRoutePaths:
|
|
668
664
|
publicRoutePaths.length === 0 ? undefined : publicRoutePaths,
|
|
669
|
-
//
|
|
670
|
-
//
|
|
671
|
-
|
|
665
|
+
// Deliberately NO manifest here: the control plane captures it from the
|
|
666
|
+
// provisioned engine (`GET /v1/manifest` after the health probe), which
|
|
667
|
+
// is the single write path for deploy manifests. `july manifest` remains
|
|
668
|
+
// available for local visibility only.
|
|
672
669
|
};
|
|
673
670
|
|
|
674
671
|
let accepted: DeployAccepted;
|
|
@@ -864,13 +861,6 @@ interface DeployManifest {
|
|
|
864
861
|
* (only `/v1/channels/…` subtrees can be public); surfaced as a warning.
|
|
865
862
|
*/
|
|
866
863
|
httpChannelPublicEndpoint: boolean;
|
|
867
|
-
/**
|
|
868
|
-
* The offline deploy manifest (Wd): derived event declarations, channel
|
|
869
|
-
* kinds, egress, routes, and the full `/v1/info` payload. Attached to the
|
|
870
|
-
* deploy request so the control plane stores metadata before any VM
|
|
871
|
-
* starts; servers that predate manifest storage strip the field.
|
|
872
|
-
*/
|
|
873
|
-
deployManifest: AgentDeployManifestV1;
|
|
874
864
|
}
|
|
875
865
|
|
|
876
866
|
/**
|
|
@@ -895,7 +885,6 @@ async function readDeployManifest(dir: string): Promise<DeployManifest> {
|
|
|
895
885
|
httpChannelPublicEndpoint: (project.httpChannel.auth ?? []).some(
|
|
896
886
|
isPublicEndpointPolicy
|
|
897
887
|
),
|
|
898
|
-
deployManifest: buildAgentDeployManifest(project),
|
|
899
888
|
};
|
|
900
889
|
}
|
|
901
890
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { CLI_COMMAND_NAME, PACKAGE_NAME } from "../distribution.js";
|
|
10
|
+
import { MEMORY_TS } from "../init-scaffold.js";
|
|
10
11
|
import { pascalCase, SlugRegistry, slugify } from "./slug.js";
|
|
11
12
|
import type {
|
|
12
13
|
ActionJson,
|
|
@@ -128,7 +129,7 @@ export function convertWorkflow(
|
|
|
128
129
|
hasSlackAction && emitSlackAccountConnection(automation, out);
|
|
129
130
|
|
|
130
131
|
if (workflow.memoryEnabled === true) {
|
|
131
|
-
out.files.push({ path: "agent/hooks/memory.ts", contents:
|
|
132
|
+
out.files.push({ path: "agent/hooks/memory.ts", contents: MEMORY_TS });
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
out.setupSteps.push(
|
|
@@ -1410,13 +1411,3 @@ function jsonComment(value: unknown): string[] {
|
|
|
1410
1411
|
function dedupe(values: string[]): string[] {
|
|
1411
1412
|
return [...new Set(values)];
|
|
1412
1413
|
}
|
|
1413
|
-
|
|
1414
|
-
/** Memory hook contents — identical to the `initProject` scaffold. */
|
|
1415
|
-
const MEMORY_HOOK = `// Journals every turn (timestamp, session, user message, result) so
|
|
1416
|
-
// future sessions can recall past work. The journal is shared across every
|
|
1417
|
-
// session — and every user — of this agent. Pass your own MemoryBackend to
|
|
1418
|
-
// change where memories live, or delete this file to opt out.
|
|
1419
|
-
import { memoryHook } from "${PACKAGE_NAME}/memory";
|
|
1420
|
-
|
|
1421
|
-
export default memoryHook();
|
|
1422
|
-
`;
|
|
@@ -23,9 +23,22 @@ import {
|
|
|
23
23
|
import { bearerAuth } from "../../channels.js";
|
|
24
24
|
import type { AgentProject, AuthPolicy } from "../../types.js";
|
|
25
25
|
import { isCursorAccountTransport } from "../../types.js";
|
|
26
|
+
import { probeConnectorAuthUrls } from "../cursor-account-mcp-auth.js";
|
|
27
|
+
import { LOG_PREFIX } from "../distribution.js";
|
|
26
28
|
import { handleStatelessMcpRequest } from "../mcp-endpoint.js";
|
|
27
29
|
import { isLoopbackAddress } from "../net.js";
|
|
30
|
+
import {
|
|
31
|
+
sanitizeMcpToolName,
|
|
32
|
+
suffixMcpToolName,
|
|
33
|
+
} from "../sanitize-mcp-tool-name.js";
|
|
28
34
|
import type { EngineResolvedConnection } from "../session-engine.js";
|
|
35
|
+
import {
|
|
36
|
+
colorEnabledFor,
|
|
37
|
+
formatTerminalBox,
|
|
38
|
+
stdoutPalette,
|
|
39
|
+
type TerminalPalette,
|
|
40
|
+
terminalHyperlink,
|
|
41
|
+
} from "../terminal-style.js";
|
|
29
42
|
import type {
|
|
30
43
|
AccountMcpServer,
|
|
31
44
|
CursorBackendClient,
|
|
@@ -38,8 +51,13 @@ const CATALOG_TTL_MS = 60_000;
|
|
|
38
51
|
* a model hallucinating tool names cannot turn into a backend hot loop.
|
|
39
52
|
*/
|
|
40
53
|
const FORCE_REFRESH_MIN_INTERVAL_MS = 5_000;
|
|
41
|
-
|
|
42
|
-
|
|
54
|
+
|
|
55
|
+
/** Dashboard Integrations & MCP — reconnect team / account connectors. */
|
|
56
|
+
export const CURSOR_ACCOUNT_MCP_DASHBOARD_URL =
|
|
57
|
+
"https://cursor.com/dashboard/integrations";
|
|
58
|
+
/** Cursor app Settings → MCP (Connect / Authenticate on the named row). */
|
|
59
|
+
export const CURSOR_ACCOUNT_MCP_SETTINGS_DEEPLINK =
|
|
60
|
+
"cursor://anysphere.cursor-deeplink/customize?tab=mcp";
|
|
43
61
|
|
|
44
62
|
// ============================================================================
|
|
45
63
|
// Authored account MCP connections (validated refs, mirroring peer MCP connections)
|
|
@@ -131,6 +149,8 @@ interface CatalogTool {
|
|
|
131
149
|
serverIdentifier: string;
|
|
132
150
|
description: string;
|
|
133
151
|
inputSchema: Record<string, unknown>;
|
|
152
|
+
/** Present only when the connector's server declares one. */
|
|
153
|
+
outputSchema?: Record<string, unknown>;
|
|
134
154
|
}
|
|
135
155
|
|
|
136
156
|
interface Catalog {
|
|
@@ -155,6 +175,7 @@ export class CursorAccountMcpBridge {
|
|
|
155
175
|
readonly secret: string = randomBytes(32).toString("hex");
|
|
156
176
|
private readonly client: CursorBackendClient;
|
|
157
177
|
private readonly logger: (line: string) => void;
|
|
178
|
+
private readonly palette: TerminalPalette;
|
|
158
179
|
private readonly secretPolicy: AuthPolicy;
|
|
159
180
|
private readonly allowRemoteCallers: boolean;
|
|
160
181
|
private readonly catalogs = new Map<string, CatalogEntry>();
|
|
@@ -162,6 +183,8 @@ export class CursorAccountMcpBridge {
|
|
|
162
183
|
constructor(args: {
|
|
163
184
|
client: CursorBackendClient;
|
|
164
185
|
logger?: (line: string) => void;
|
|
186
|
+
/** Defaults to stdout; tests pass `makePalette(false)` for a plain box. */
|
|
187
|
+
palette?: TerminalPalette;
|
|
165
188
|
/**
|
|
166
189
|
* Admit non-loopback callers presenting the secret. Only set when the
|
|
167
190
|
* serve host configured a `publicUrl` (cloud turns reach the bridge
|
|
@@ -171,6 +194,7 @@ export class CursorAccountMcpBridge {
|
|
|
171
194
|
}) {
|
|
172
195
|
this.client = args.client;
|
|
173
196
|
this.logger = args.logger ?? (() => {});
|
|
197
|
+
this.palette = args.palette ?? stdoutPalette();
|
|
174
198
|
this.secretPolicy = bearerAuth(this.secret);
|
|
175
199
|
this.allowRemoteCallers = args.allowRemoteCallers === true;
|
|
176
200
|
}
|
|
@@ -213,9 +237,9 @@ export class CursorAccountMcpBridge {
|
|
|
213
237
|
|
|
214
238
|
/**
|
|
215
239
|
* Startup DX: log the account's connector statuses for `refs` without
|
|
216
|
-
* blocking serve startup.
|
|
217
|
-
*
|
|
218
|
-
*
|
|
240
|
+
* blocking serve startup. Healthy catalogs stay a one-line summary;
|
|
241
|
+
* `needsAuth` / `error` / empty catalogs render a yellow warning box with
|
|
242
|
+
* reconnect URLs.
|
|
219
243
|
*
|
|
220
244
|
* Returns a promise that settles when every ref's log lines are written —
|
|
221
245
|
* callers may ignore it; tests can await it.
|
|
@@ -229,28 +253,50 @@ export class CursorAccountMcpBridge {
|
|
|
229
253
|
private async runPreflight(ref: CursorAccountConnectionRef): Promise<void> {
|
|
230
254
|
try {
|
|
231
255
|
const catalog = await this.getCatalog(ref);
|
|
232
|
-
|
|
256
|
+
const errorDetails = new Map<string, string>();
|
|
257
|
+
const needsAuthNames = catalog.servers
|
|
258
|
+
.filter((server) => server.status === "needsAuth")
|
|
259
|
+
.map((server) => server.serverIdentifier);
|
|
260
|
+
const [authUrls] = await Promise.all([
|
|
261
|
+
probeConnectorAuthUrls({
|
|
262
|
+
client: this.client,
|
|
263
|
+
identifiers: needsAuthNames,
|
|
264
|
+
}),
|
|
265
|
+
Promise.all(
|
|
266
|
+
catalog.servers
|
|
267
|
+
.filter((server) => server.status === "error")
|
|
268
|
+
.map(async (server) => {
|
|
269
|
+
const detail = await diagnoseConnectorError(
|
|
270
|
+
this.client,
|
|
271
|
+
server.serverIdentifier
|
|
272
|
+
);
|
|
273
|
+
if (detail !== undefined) {
|
|
274
|
+
errorDetails.set(server.serverIdentifier, detail);
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
),
|
|
278
|
+
]);
|
|
279
|
+
|
|
280
|
+
const unhealthy = catalog.servers.some(
|
|
281
|
+
(server) => server.status === "needsAuth" || server.status === "error"
|
|
282
|
+
);
|
|
283
|
+
if (catalog.servers.length === 0 || unhealthy) {
|
|
233
284
|
this.logger(
|
|
234
|
-
|
|
285
|
+
formatCursorAccountMcpWarningBox({
|
|
286
|
+
connectionName: ref.name,
|
|
287
|
+
servers: catalog.servers.map((server) => ({
|
|
288
|
+
serverIdentifier: server.serverIdentifier,
|
|
289
|
+
status: server.status,
|
|
290
|
+
toolCount: server.tools.length,
|
|
291
|
+
errorDetail: errorDetails.get(server.serverIdentifier),
|
|
292
|
+
authUrl: authUrls.get(server.serverIdentifier),
|
|
293
|
+
})),
|
|
294
|
+
palette: this.palette,
|
|
295
|
+
})
|
|
235
296
|
);
|
|
236
297
|
return;
|
|
237
298
|
}
|
|
238
299
|
|
|
239
|
-
const errorDetails = new Map<string, string>();
|
|
240
|
-
await Promise.all(
|
|
241
|
-
catalog.servers
|
|
242
|
-
.filter((server) => server.status === "error")
|
|
243
|
-
.map(async (server) => {
|
|
244
|
-
const detail = await diagnoseConnectorError(
|
|
245
|
-
this.client,
|
|
246
|
-
server.serverIdentifier
|
|
247
|
-
);
|
|
248
|
-
if (detail !== undefined) {
|
|
249
|
-
errorDetails.set(server.serverIdentifier, detail);
|
|
250
|
-
}
|
|
251
|
-
})
|
|
252
|
-
);
|
|
253
|
-
|
|
254
300
|
const summary = catalog.servers
|
|
255
301
|
.map((server) =>
|
|
256
302
|
formatServerStatusSummary(
|
|
@@ -260,41 +306,11 @@ export class CursorAccountMcpBridge {
|
|
|
260
306
|
)
|
|
261
307
|
.join(", ");
|
|
262
308
|
this.logger(
|
|
263
|
-
|
|
264
|
-
);
|
|
265
|
-
|
|
266
|
-
const needsAuth = catalog.servers.filter(
|
|
267
|
-
(server) => server.status === "needsAuth"
|
|
309
|
+
`${LOG_PREFIX} Cursor account MCP connection "${ref.name}": ${summary}`
|
|
268
310
|
);
|
|
269
|
-
if (needsAuth.length > 0) {
|
|
270
|
-
this.logger(
|
|
271
|
-
`[agent-sdk] Cursor account MCP connection "${ref.name}": ${needsAuth
|
|
272
|
-
.map((server) => server.serverIdentifier)
|
|
273
|
-
.join(
|
|
274
|
-
", "
|
|
275
|
-
)} need re-authorization in Cursor (dashboard → MCP / Settings → MCP) before their tools appear`
|
|
276
|
-
);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const errored = catalog.servers.filter(
|
|
280
|
-
(server) => server.status === "error"
|
|
281
|
-
);
|
|
282
|
-
if (errored.length > 0) {
|
|
283
|
-
const details = errored
|
|
284
|
-
.map((server) => {
|
|
285
|
-
const detail = errorDetails.get(server.serverIdentifier);
|
|
286
|
-
return detail === undefined
|
|
287
|
-
? server.serverIdentifier
|
|
288
|
-
: `${server.serverIdentifier} (${detail})`;
|
|
289
|
-
})
|
|
290
|
-
.join("; ");
|
|
291
|
-
this.logger(
|
|
292
|
-
`[agent-sdk] Cursor account MCP connection "${ref.name}": ${details} — Cursor backend could not use this connector (not a missing agent-sdk login). Re-check the server in Cursor dashboard → MCP, confirm the URL is healthy, and retry after the backend can reach it`
|
|
293
|
-
);
|
|
294
|
-
}
|
|
295
311
|
} catch (error: unknown) {
|
|
296
312
|
this.logger(
|
|
297
|
-
|
|
313
|
+
`${LOG_PREFIX} Cursor account MCP connection "${ref.name}" preflight failed: ${describeError(error)}`
|
|
298
314
|
);
|
|
299
315
|
}
|
|
300
316
|
}
|
|
@@ -316,6 +332,9 @@ export class CursorAccountMcpBridge {
|
|
|
316
332
|
name: tool.exposedName,
|
|
317
333
|
description: tool.description,
|
|
318
334
|
inputSchema: tool.inputSchema,
|
|
335
|
+
...(tool.outputSchema === undefined
|
|
336
|
+
? {}
|
|
337
|
+
: { outputSchema: tool.outputSchema }),
|
|
319
338
|
})),
|
|
320
339
|
};
|
|
321
340
|
});
|
|
@@ -448,7 +467,7 @@ function buildCatalog(servers: AccountMcpServer[]): Catalog {
|
|
|
448
467
|
>();
|
|
449
468
|
for (const server of servers) {
|
|
450
469
|
for (const tool of server.tools) {
|
|
451
|
-
const base =
|
|
470
|
+
const base = sanitizeMcpToolName(tool.name);
|
|
452
471
|
const group = byBase.get(base);
|
|
453
472
|
const item = { serverIdentifier: server.serverIdentifier, tool };
|
|
454
473
|
if (group === undefined) {
|
|
@@ -477,10 +496,10 @@ function buildCatalog(servers: AccountMcpServer[]): Catalog {
|
|
|
477
496
|
});
|
|
478
497
|
group.forEach(({ serverIdentifier, tool }, index) => {
|
|
479
498
|
let suffixIndex = index;
|
|
480
|
-
let exposedName =
|
|
499
|
+
let exposedName = suffixMcpToolName(base, suffixIndex);
|
|
481
500
|
while (byExposedName.has(exposedName)) {
|
|
482
501
|
suffixIndex++;
|
|
483
|
-
exposedName =
|
|
502
|
+
exposedName = suffixMcpToolName(base, suffixIndex);
|
|
484
503
|
}
|
|
485
504
|
const entry: CatalogTool = {
|
|
486
505
|
exposedName,
|
|
@@ -488,6 +507,9 @@ function buildCatalog(servers: AccountMcpServer[]): Catalog {
|
|
|
488
507
|
serverIdentifier,
|
|
489
508
|
description: tool.description,
|
|
490
509
|
inputSchema: tool.inputSchema,
|
|
510
|
+
...(tool.outputSchema === undefined
|
|
511
|
+
? {}
|
|
512
|
+
: { outputSchema: tool.outputSchema }),
|
|
491
513
|
};
|
|
492
514
|
tools.push(entry);
|
|
493
515
|
byExposedName.set(entry.exposedName, entry);
|
|
@@ -496,29 +518,142 @@ function buildCatalog(servers: AccountMcpServer[]): Catalog {
|
|
|
496
518
|
return { servers, tools, byExposedName };
|
|
497
519
|
}
|
|
498
520
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
return
|
|
521
|
+
function trimTrailingSlash(url: string): string {
|
|
522
|
+
return url.endsWith("/") ? url.slice(0, -1) : url;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function describeError(error: unknown): string {
|
|
526
|
+
return error instanceof Error ? error.message : String(error);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
export interface CursorAccountMcpWarningServer {
|
|
530
|
+
serverIdentifier: string;
|
|
531
|
+
status: string;
|
|
532
|
+
toolCount: number;
|
|
533
|
+
errorDetail?: string;
|
|
534
|
+
/** Minted OAuth URL for a `needsAuth` connector, when the probe succeeded. */
|
|
535
|
+
authUrl?: string;
|
|
505
536
|
}
|
|
506
537
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
538
|
+
const MAX_ERROR_DETAIL_CHARS = 72;
|
|
539
|
+
|
|
540
|
+
function truncateDetail(detail: string): string {
|
|
541
|
+
const trimmed = detail.trim();
|
|
542
|
+
if (trimmed.length <= MAX_ERROR_DETAIL_CHARS) {
|
|
543
|
+
return trimmed;
|
|
511
544
|
}
|
|
512
|
-
|
|
513
|
-
return `${base.slice(0, MAX_TOOL_NAME_LENGTH - suffix.length)}${suffix}`;
|
|
545
|
+
return `${trimmed.slice(0, MAX_ERROR_DETAIL_CHARS - 1)}…`;
|
|
514
546
|
}
|
|
515
547
|
|
|
516
|
-
function
|
|
517
|
-
|
|
548
|
+
function formatWarningStatusRow(
|
|
549
|
+
server: CursorAccountMcpWarningServer,
|
|
550
|
+
nameWidth: number,
|
|
551
|
+
hyperlinks: boolean
|
|
552
|
+
): string {
|
|
553
|
+
const name = server.serverIdentifier.padEnd(nameWidth);
|
|
554
|
+
switch (server.status) {
|
|
555
|
+
case "needsAuth":
|
|
556
|
+
return `${name} ${terminalHyperlink(connectUrl(server.authUrl), "Connect", hyperlinks)}`;
|
|
557
|
+
case "connected":
|
|
558
|
+
return `${name} ${server.toolCount} tools`;
|
|
559
|
+
case "error": {
|
|
560
|
+
const detail =
|
|
561
|
+
server.errorDetail === undefined || server.errorDetail.trim() === ""
|
|
562
|
+
? "backend could not reach connector"
|
|
563
|
+
: truncateDetail(server.errorDetail);
|
|
564
|
+
return `${name} error: ${detail}`;
|
|
565
|
+
}
|
|
566
|
+
default:
|
|
567
|
+
return `${name} ${server.status}`;
|
|
568
|
+
}
|
|
518
569
|
}
|
|
519
570
|
|
|
520
|
-
function
|
|
521
|
-
|
|
571
|
+
function connectUrl(authUrl: string | undefined): string {
|
|
572
|
+
const minted = authUrl?.trim() ?? "";
|
|
573
|
+
if (
|
|
574
|
+
minted.startsWith("https://") &&
|
|
575
|
+
!minted.includes("\u001b") &&
|
|
576
|
+
!minted.includes("\u0007")
|
|
577
|
+
) {
|
|
578
|
+
return minted;
|
|
579
|
+
}
|
|
580
|
+
return CURSOR_ACCOUNT_MCP_DASHBOARD_URL;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Yellow warning panel for unhealthy / missing Cursor-account connectors.
|
|
585
|
+
* One box replaces the old stacked `[agent-sdk]` log lines.
|
|
586
|
+
*/
|
|
587
|
+
export function formatCursorAccountMcpWarningBox(args: {
|
|
588
|
+
connectionName: string;
|
|
589
|
+
servers: readonly CursorAccountMcpWarningServer[];
|
|
590
|
+
palette?: TerminalPalette;
|
|
591
|
+
hyperlinks?: boolean;
|
|
592
|
+
}): string {
|
|
593
|
+
const palette = args.palette ?? stdoutPalette();
|
|
594
|
+
const hyperlinks = args.hyperlinks ?? colorEnabledFor();
|
|
595
|
+
const copy = warningCopy({ ...args, hyperlinks });
|
|
596
|
+
return `\n${formatTerminalBox(
|
|
597
|
+
[copy.title, "", ...copy.intro, ...copy.actions],
|
|
598
|
+
{
|
|
599
|
+
color: palette.yellow,
|
|
600
|
+
padding: 2,
|
|
601
|
+
}
|
|
602
|
+
)}`;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function warningCopy(args: {
|
|
606
|
+
connectionName: string;
|
|
607
|
+
servers: readonly CursorAccountMcpWarningServer[];
|
|
608
|
+
hyperlinks: boolean;
|
|
609
|
+
}): { title: string; intro: string[]; actions: string[] } {
|
|
610
|
+
const empty = args.servers.length === 0;
|
|
611
|
+
const hasAuth = args.servers.some((server) => server.status === "needsAuth");
|
|
612
|
+
const hasError = args.servers.some((server) => server.status === "error");
|
|
613
|
+
const nameWidth = Math.max(
|
|
614
|
+
0,
|
|
615
|
+
...args.servers.map((server) => server.serverIdentifier.length)
|
|
616
|
+
);
|
|
617
|
+
const statusRows = args.servers.map((server) =>
|
|
618
|
+
formatWarningStatusRow(server, nameWidth, args.hyperlinks)
|
|
619
|
+
);
|
|
620
|
+
const links = [
|
|
621
|
+
` ${CURSOR_ACCOUNT_MCP_SETTINGS_DEEPLINK}`,
|
|
622
|
+
` ${CURSOR_ACCOUNT_MCP_DASHBOARD_URL}`,
|
|
623
|
+
];
|
|
624
|
+
const restart = "Then press Enter to restart this serve.";
|
|
625
|
+
|
|
626
|
+
if (empty) {
|
|
627
|
+
return {
|
|
628
|
+
title: "No matching MCP connectors",
|
|
629
|
+
intro: [
|
|
630
|
+
`No matching connectors on this account ("${args.connectionName}").`,
|
|
631
|
+
],
|
|
632
|
+
actions: [
|
|
633
|
+
"",
|
|
634
|
+
"Connect servers in Cursor, or fix the `servers` filter.",
|
|
635
|
+
...links,
|
|
636
|
+
],
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
if (hasAuth && !hasError) {
|
|
640
|
+
return {
|
|
641
|
+
title: "MCP authorization needed",
|
|
642
|
+
intro: statusRows,
|
|
643
|
+
actions: ["", restart, "", "Or reconnect at:", ...links],
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
return {
|
|
647
|
+
title: hasAuth ? "MCP authorization needed" : "MCP connector error",
|
|
648
|
+
intro: statusRows,
|
|
649
|
+
actions: [
|
|
650
|
+
"",
|
|
651
|
+
"Re-check the server URL is healthy, then reconnect at:",
|
|
652
|
+
...links,
|
|
653
|
+
"",
|
|
654
|
+
restart,
|
|
655
|
+
],
|
|
656
|
+
};
|
|
522
657
|
}
|
|
523
658
|
|
|
524
659
|
/** One-line status for the preflight summary (`name (12 tools)` / `name (error: …)`). */
|