@cursor/july 0.1.87 → 0.1.89
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/bin/agent-serve.js +8 -1
- package/dist/internal/advertise-tools.d.ts.map +1 -1
- package/dist/internal/advertise-tools.js +4 -2
- package/dist/internal/cli-ax.js +1 -1
- package/dist/internal/cli-deploy.d.ts +3 -2
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +84 -9
- package/dist/internal/cli-docs.js +11 -0
- package/dist/internal/cloud-merge.d.ts +3 -1
- package/dist/internal/cloud-merge.d.ts.map +1 -1
- package/dist/internal/cloud-merge.js +10 -2
- package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
- package/dist/internal/cursor/account-mcp.js +8 -4
- package/dist/internal/cursor/backend-client.d.ts +12 -0
- package/dist/internal/cursor/backend-client.d.ts.map +1 -1
- package/dist/internal/cursor/backend-client.js +27 -1
- package/dist/internal/deploy-client.d.ts +42 -1
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +70 -2
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +87 -11
- package/dist/internal/docs-site.d.ts +13 -2
- package/dist/internal/docs-site.d.ts.map +1 -1
- package/dist/internal/docs-site.js +76 -13
- package/dist/internal/grokbot/runner.d.ts +61 -0
- package/dist/internal/grokbot/runner.d.ts.map +1 -0
- package/dist/internal/grokbot/runner.js +278 -0
- package/dist/internal/mcp-endpoint.js +6 -3
- package/dist/internal/mcp-host.d.ts +14 -1
- package/dist/internal/mcp-host.d.ts.map +1 -1
- package/dist/internal/mcp-host.js +41 -2
- package/dist/internal/runtime-dispatch-runner.d.ts +30 -0
- package/dist/internal/runtime-dispatch-runner.d.ts.map +1 -0
- package/dist/internal/runtime-dispatch-runner.js +60 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +7 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +24 -1
- package/dist/internal/session-engine.d.ts +9 -2
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +121 -46
- package/dist/internal/tool-policy.d.ts +33 -1
- package/dist/internal/tool-policy.d.ts.map +1 -1
- package/dist/internal/tool-policy.js +53 -0
- package/dist/playground/assets/{index-Bqn91tW4.js → index-BmiIjFlM.js} +45 -45
- package/dist/playground/assets/index-DQGZnAI0.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +86 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/skills/debug/SKILL.md +1 -2
- package/skills/framework-map/SKILL.md +1 -2
- package/src/bin/agent-serve.ts +9 -1
- package/src/internal/advertise-tools.ts +7 -0
- package/src/internal/cli-ax.ts +1 -1
- package/src/internal/cli-deploy.ts +123 -15
- package/src/internal/cli-docs.ts +11 -0
- package/src/internal/cloud-merge.ts +12 -2
- package/src/internal/cursor/account-mcp.ts +15 -1
- package/src/internal/cursor/backend-client.ts +37 -0
- package/src/internal/deploy-client.ts +122 -2
- package/src/internal/discovery.ts +133 -13
- package/src/internal/docs-site.ts +83 -13
- package/src/internal/grokbot/runner.ts +361 -0
- package/src/internal/mcp-endpoint.ts +6 -1
- package/src/internal/mcp-host.ts +48 -0
- package/src/internal/runtime-dispatch-runner.ts +63 -0
- package/src/internal/sdk-runner.ts +9 -0
- package/src/internal/server.ts +32 -4
- package/src/internal/session-engine.ts +126 -21
- package/src/internal/tool-policy.ts +80 -1
- package/src/types.ts +84 -2
- package/dist/playground/assets/index-C0_5hOsf.css +0 -1
|
@@ -5,13 +5,15 @@
|
|
|
5
5
|
*
|
|
6
6
|
* All intelligence is server-side (backend/server/src/agentServe/); these
|
|
7
7
|
* handlers resolve parameters (credentials, team, slug, the project's
|
|
8
|
-
* `hosting` manifest block), call the
|
|
9
|
-
* `deploy-client.ts`, poll status, and
|
|
8
|
+
* `hosting` / `architecture` manifest block), call the v1 agent-serve or
|
|
9
|
+
* v2 agentkit management API through `deploy-client.ts`, poll status, and
|
|
10
|
+
* render.
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
import { basename, resolve } from "node:path";
|
|
13
14
|
import { isPublicEndpointPolicy } from "../channels.js";
|
|
14
15
|
import {
|
|
16
|
+
type AgentArchitecture,
|
|
15
17
|
type AgentHostingOptions,
|
|
16
18
|
isCursorAccountTransport,
|
|
17
19
|
} from "../types.js";
|
|
@@ -24,18 +26,21 @@ import {
|
|
|
24
26
|
import { authorizeCursorAccountConnectors } from "./cursor-account-mcp-auth.js";
|
|
25
27
|
import { readDeployTemplate } from "./cursor-agent-template.js";
|
|
26
28
|
import {
|
|
29
|
+
type AgentkitDeployAccepted,
|
|
30
|
+
type AgentkitDeployStatus,
|
|
27
31
|
AgentServeDeployClient,
|
|
28
32
|
type DeleteAccepted,
|
|
29
33
|
type DeployAccepted,
|
|
30
34
|
DeployApiError,
|
|
31
35
|
type Deployment,
|
|
32
36
|
type DeploymentDetail,
|
|
33
|
-
type
|
|
37
|
+
type DeploymentProgress,
|
|
34
38
|
type DeploySource,
|
|
35
39
|
deploymentServiceAccountId,
|
|
36
40
|
type SecretInfo,
|
|
37
41
|
type StopAccepted,
|
|
38
42
|
slugFromDirectoryName,
|
|
43
|
+
TERMINAL_AGENTKIT_DEPLOY_STATUSES,
|
|
39
44
|
TERMINAL_DEPLOY_STATUSES,
|
|
40
45
|
validateDeploymentSlug,
|
|
41
46
|
} from "./deploy-client.js";
|
|
@@ -648,6 +653,19 @@ async function deployOne(
|
|
|
648
653
|
);
|
|
649
654
|
}
|
|
650
655
|
|
|
656
|
+
if (manifest?.architecture === "v2") {
|
|
657
|
+
if (!ctx.json) {
|
|
658
|
+
ctx.err(" architecture: v2\n");
|
|
659
|
+
}
|
|
660
|
+
return deployOneAgentkit(ctx, extras, {
|
|
661
|
+
slug,
|
|
662
|
+
gitRepoUrl,
|
|
663
|
+
gitRef,
|
|
664
|
+
agentPath,
|
|
665
|
+
repositories: options.cursorEventsRepos,
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
|
|
651
669
|
// Today's only source shape: the control plane builds from a git ref.
|
|
652
670
|
// See DeploySource for the artifact seam.
|
|
653
671
|
const source: DeploySource = {
|
|
@@ -702,7 +720,9 @@ async function deployOne(
|
|
|
702
720
|
|
|
703
721
|
let detail: DeploymentDetail;
|
|
704
722
|
try {
|
|
705
|
-
detail = await pollUntil(ctx, slug, TERMINAL_DEPLOY_STATUSES)
|
|
723
|
+
detail = await pollUntil(ctx, slug, TERMINAL_DEPLOY_STATUSES, () =>
|
|
724
|
+
ctx.client.getDeployment({ teamId: ctx.teamId, slug })
|
|
725
|
+
);
|
|
706
726
|
} catch (error) {
|
|
707
727
|
return renderApiError(error, ctx.err);
|
|
708
728
|
}
|
|
@@ -756,6 +776,85 @@ async function deployOne(
|
|
|
756
776
|
return 0;
|
|
757
777
|
}
|
|
758
778
|
|
|
779
|
+
/**
|
|
780
|
+
* `architecture: "v2"` deploy: POST `/internal/agentkit/deployments` and
|
|
781
|
+
* poll the matching GET. The v2 accept path starts freeze bake; it does
|
|
782
|
+
* not mint an engine alias or alias token.
|
|
783
|
+
*/
|
|
784
|
+
async function deployOneAgentkit(
|
|
785
|
+
ctx: DeployCliContext,
|
|
786
|
+
extras: { jsonCollect?: unknown[] } | undefined,
|
|
787
|
+
args: {
|
|
788
|
+
slug: string;
|
|
789
|
+
gitRepoUrl: string;
|
|
790
|
+
gitRef: string | undefined;
|
|
791
|
+
agentPath: string | undefined;
|
|
792
|
+
repositories: string[] | undefined;
|
|
793
|
+
}
|
|
794
|
+
): Promise<number> {
|
|
795
|
+
const { slug } = args;
|
|
796
|
+
let accepted: AgentkitDeployAccepted;
|
|
797
|
+
try {
|
|
798
|
+
accepted = await ctx.client.deployAgentkit({
|
|
799
|
+
teamId: ctx.teamId,
|
|
800
|
+
slug,
|
|
801
|
+
gitRepoUrl: args.gitRepoUrl,
|
|
802
|
+
gitRef: args.gitRef,
|
|
803
|
+
agentPath: args.agentPath,
|
|
804
|
+
repositories: args.repositories,
|
|
805
|
+
});
|
|
806
|
+
} catch (error) {
|
|
807
|
+
return renderApiError(error, ctx.err);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
if (ctx.noWait) {
|
|
811
|
+
if (extras?.jsonCollect !== undefined) {
|
|
812
|
+
extras.jsonCollect.push(accepted);
|
|
813
|
+
} else if (ctx.json) {
|
|
814
|
+
ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
|
|
815
|
+
} else {
|
|
816
|
+
ctx.out(
|
|
817
|
+
`Deploy accepted: ${slug} (team ${ctx.teamId}, v2)\n` +
|
|
818
|
+
` application: ${accepted.applicationId}\n` +
|
|
819
|
+
` release: ${accepted.releaseId}\n`
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
return 0;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
let detail: AgentkitDeployStatus;
|
|
826
|
+
try {
|
|
827
|
+
detail = await pollUntil(ctx, slug, TERMINAL_AGENTKIT_DEPLOY_STATUSES, () =>
|
|
828
|
+
ctx.client.getAgentkitDeploy({ teamId: ctx.teamId, slug })
|
|
829
|
+
);
|
|
830
|
+
} catch (error) {
|
|
831
|
+
return renderApiError(error, ctx.err);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
if (extras?.jsonCollect !== undefined) {
|
|
835
|
+
extras.jsonCollect.push(detail);
|
|
836
|
+
return detail.status === "running" ? 0 : 1;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
if (ctx.json) {
|
|
840
|
+
ctx.out(`${JSON.stringify(detail, null, 2)}\n`);
|
|
841
|
+
return detail.status === "running" ? 0 : 1;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
if (detail.status !== "running") {
|
|
845
|
+
ctx.err(`Deployment ${slug} ended ${detail.status}\n`);
|
|
846
|
+
return 1;
|
|
847
|
+
}
|
|
848
|
+
ctx.out(
|
|
849
|
+
`Deployed ${slug} (team ${ctx.teamId})\n` +
|
|
850
|
+
` status: running\n` +
|
|
851
|
+
` architecture: v2\n` +
|
|
852
|
+
` application: ${detail.applicationId}\n` +
|
|
853
|
+
` release: ${detail.releaseId}\n`
|
|
854
|
+
);
|
|
855
|
+
return 0;
|
|
856
|
+
}
|
|
857
|
+
|
|
759
858
|
/**
|
|
760
859
|
* Post-deploy connector authorization for `cursorAccount` connections: the
|
|
761
860
|
* agent's connectors execute with tokens owned by the deployment's service
|
|
@@ -845,6 +944,7 @@ async function runPostDeployConnectorAuth(
|
|
|
845
944
|
|
|
846
945
|
interface DeployManifest {
|
|
847
946
|
hosting: AgentHostingOptions | undefined;
|
|
947
|
+
architecture: AgentArchitecture | undefined;
|
|
848
948
|
/** `cursorAccount` connections declared by the agent. */
|
|
849
949
|
cursorAccountConnections: Array<{
|
|
850
950
|
name: string;
|
|
@@ -872,6 +972,7 @@ async function readDeployManifest(dir: string): Promise<DeployManifest> {
|
|
|
872
972
|
const project = await loadAgentProject(dir);
|
|
873
973
|
return {
|
|
874
974
|
hosting: project.agent.hosting,
|
|
975
|
+
architecture: project.agent.architecture,
|
|
875
976
|
cursorAccountConnections: project.agent.connections.flatMap((connection) =>
|
|
876
977
|
isCursorAccountTransport(connection.transport)
|
|
877
978
|
? [{ name: connection.name, servers: connection.transport.servers }]
|
|
@@ -1102,7 +1203,9 @@ export async function cmdStop(
|
|
|
1102
1203
|
}
|
|
1103
1204
|
let detail: DeploymentDetail;
|
|
1104
1205
|
try {
|
|
1105
|
-
detail = await pollUntil(ctx, slug, new Set(["stopped"]))
|
|
1206
|
+
detail = await pollUntil(ctx, slug, new Set(["stopped"]), () =>
|
|
1207
|
+
ctx.client.getDeployment({ teamId: ctx.teamId, slug })
|
|
1208
|
+
);
|
|
1106
1209
|
} catch (error) {
|
|
1107
1210
|
return renderApiError(error, ctx.err);
|
|
1108
1211
|
}
|
|
@@ -1517,17 +1620,24 @@ export function createLineReader(
|
|
|
1517
1620
|
* status so create → checkout → start is visible. Throws DeployApiError
|
|
1518
1621
|
* on request failures or timeout.
|
|
1519
1622
|
*/
|
|
1520
|
-
async function pollUntil
|
|
1623
|
+
async function pollUntil<
|
|
1624
|
+
T extends {
|
|
1625
|
+
status: string;
|
|
1626
|
+
progress?: DeploymentProgress;
|
|
1627
|
+
statusMessage?: string | null;
|
|
1628
|
+
},
|
|
1629
|
+
>(
|
|
1521
1630
|
ctx: DeployCliContext,
|
|
1522
1631
|
slug: string,
|
|
1523
|
-
terminal: ReadonlySet<
|
|
1524
|
-
|
|
1632
|
+
terminal: ReadonlySet<T["status"]>,
|
|
1633
|
+
fetchDetail: () => Promise<T>
|
|
1634
|
+
): Promise<T> {
|
|
1525
1635
|
const startedAt = Date.now();
|
|
1526
1636
|
const tty = process.stderr.isTTY === true;
|
|
1527
1637
|
let lastLabel: string | undefined;
|
|
1528
1638
|
let sawProgress = false;
|
|
1529
1639
|
|
|
1530
|
-
const report = (detail:
|
|
1640
|
+
const report = (detail: T): void => {
|
|
1531
1641
|
if (ctx.json) {
|
|
1532
1642
|
return;
|
|
1533
1643
|
}
|
|
@@ -1548,12 +1658,10 @@ async function pollUntil(
|
|
|
1548
1658
|
};
|
|
1549
1659
|
|
|
1550
1660
|
for (;;) {
|
|
1551
|
-
const detail = await
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
throw error;
|
|
1556
|
-
});
|
|
1661
|
+
const detail = await fetchDetail().catch((error: unknown) => {
|
|
1662
|
+
finishProgressLine();
|
|
1663
|
+
throw error;
|
|
1664
|
+
});
|
|
1557
1665
|
report(detail);
|
|
1558
1666
|
if (terminal.has(detail.status)) {
|
|
1559
1667
|
finishProgressLine();
|
package/src/internal/cli-docs.ts
CHANGED
|
@@ -162,6 +162,17 @@ async function handleDocsRequest(
|
|
|
162
162
|
res.end();
|
|
163
163
|
return;
|
|
164
164
|
}
|
|
165
|
+
if (pathname === "/v1/health" || pathname === "/v1/health/") {
|
|
166
|
+
// Same path the corp ALB / ECS check uses on agent-serve. Static docs
|
|
167
|
+
// has no runtimes; liveness is "the process accepted HTTP".
|
|
168
|
+
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
169
|
+
if (method === "HEAD") {
|
|
170
|
+
res.end();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
res.end(JSON.stringify({ ok: true }));
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
165
176
|
let requestPath = "";
|
|
166
177
|
if (pathname === "/docs" || pathname === "/docs/") {
|
|
167
178
|
requestPath = "";
|
|
@@ -31,11 +31,21 @@ export function mergeCloudOptions(args: {
|
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Session turn runtime: a per-session `cloud` attach forces cloud even when
|
|
34
|
-
* the agent defaults to local (chat server tools + Drive handoff).
|
|
34
|
+
* the agent defaults to local (chat server tools + Drive handoff). A
|
|
35
|
+
* grokbot agent refuses the attach — silently moving its session onto a
|
|
36
|
+
* cloud VM would swap the harness (and the box) out from under it.
|
|
35
37
|
*/
|
|
36
38
|
export function resolveSessionRuntime(args: {
|
|
37
39
|
agentRuntime: AgentRuntime;
|
|
38
40
|
cloudOverride: AgentCloudOptions | undefined;
|
|
39
41
|
}): AgentRuntime {
|
|
40
|
-
|
|
42
|
+
if (args.cloudOverride === undefined) {
|
|
43
|
+
return args.agentRuntime;
|
|
44
|
+
}
|
|
45
|
+
if (args.agentRuntime === "grokbot") {
|
|
46
|
+
throw new Error(
|
|
47
|
+
'Grok Bot sessions cannot attach cloud agents: per-session `cloud` options are not supported on runtime: "grokbot". Refusing the session rather than moving it onto a cloud VM.'
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
return "cloud";
|
|
41
51
|
}
|
|
@@ -21,7 +21,11 @@ import {
|
|
|
21
21
|
ListToolsRequestSchema,
|
|
22
22
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
23
23
|
import { bearerAuth } from "../../channels.js";
|
|
24
|
-
import type {
|
|
24
|
+
import type {
|
|
25
|
+
AgentProject,
|
|
26
|
+
AuthPolicy,
|
|
27
|
+
McpToolAnnotations,
|
|
28
|
+
} from "../../types.js";
|
|
25
29
|
import { isCursorAccountTransport } from "../../types.js";
|
|
26
30
|
import { probeConnectorAuthUrls } from "../cursor-account-mcp-auth.js";
|
|
27
31
|
import { LOG_PREFIX } from "../distribution.js";
|
|
@@ -151,6 +155,8 @@ interface CatalogTool {
|
|
|
151
155
|
inputSchema: Record<string, unknown>;
|
|
152
156
|
/** Present only when the connector's server declares one. */
|
|
153
157
|
outputSchema?: Record<string, unknown>;
|
|
158
|
+
/** MCP spec annotations, present only when the server declared them. */
|
|
159
|
+
annotations?: McpToolAnnotations;
|
|
154
160
|
}
|
|
155
161
|
|
|
156
162
|
interface Catalog {
|
|
@@ -335,6 +341,11 @@ export class CursorAccountMcpBridge {
|
|
|
335
341
|
...(tool.outputSchema === undefined
|
|
336
342
|
? {}
|
|
337
343
|
: { outputSchema: tool.outputSchema }),
|
|
344
|
+
// Already bounded to the MCP-spec fields at the backend-client
|
|
345
|
+
// parse; absent stays absent so classifiers fail closed.
|
|
346
|
+
...(tool.annotations === undefined
|
|
347
|
+
? {}
|
|
348
|
+
: { annotations: tool.annotations }),
|
|
338
349
|
})),
|
|
339
350
|
};
|
|
340
351
|
});
|
|
@@ -510,6 +521,9 @@ function buildCatalog(servers: AccountMcpServer[]): Catalog {
|
|
|
510
521
|
...(tool.outputSchema === undefined
|
|
511
522
|
? {}
|
|
512
523
|
: { outputSchema: tool.outputSchema }),
|
|
524
|
+
...(tool.annotations === undefined
|
|
525
|
+
? {}
|
|
526
|
+
: { annotations: tool.annotations }),
|
|
513
527
|
};
|
|
514
528
|
tools.push(entry);
|
|
515
529
|
byExposedName.set(entry.exposedName, entry);
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
import { randomUUID } from "node:crypto";
|
|
17
17
|
import { z } from "zod";
|
|
18
|
+
import type { McpToolAnnotations } from "../../types.js";
|
|
19
|
+
import { boundedToolAnnotations } from "../mcp-host.js";
|
|
18
20
|
|
|
19
21
|
const CLIENT_VERSION = "agent-serve-0.1.0";
|
|
20
22
|
const EXCHANGE_PATH = "/auth/exchange_user_api_key";
|
|
@@ -85,6 +87,8 @@ const mcpToolSchema = z.object({
|
|
|
85
87
|
inputSchema: z.unknown().optional(),
|
|
86
88
|
/** Flat JSON-string output schema (`output_schema_json` on the proto). */
|
|
87
89
|
outputSchemaJson: z.string().optional(),
|
|
90
|
+
/** Flat JSON-string MCP annotations (`annotations_json` on the proto). */
|
|
91
|
+
annotationsJson: z.string().optional(),
|
|
88
92
|
});
|
|
89
93
|
|
|
90
94
|
const getAvailableMcpServersResponseSchema = z.object({
|
|
@@ -154,6 +158,13 @@ export interface AccountMcpTool {
|
|
|
154
158
|
* matching the optional MCP `Tool.outputSchema` field.
|
|
155
159
|
*/
|
|
156
160
|
outputSchema?: Record<string, unknown>;
|
|
161
|
+
/**
|
|
162
|
+
* MCP spec tool annotations as the connector's server reported them,
|
|
163
|
+
* bounded to the spec fields at parse time. Absent stays absent —
|
|
164
|
+
* untrusted hints, and the fail-closed signal for consumers that classify
|
|
165
|
+
* on them (dry-run effect resolution).
|
|
166
|
+
*/
|
|
167
|
+
annotations?: McpToolAnnotations;
|
|
157
168
|
}
|
|
158
169
|
|
|
159
170
|
export interface AccountMcpServer {
|
|
@@ -331,11 +342,13 @@ export class CursorBackendClient {
|
|
|
331
342
|
status: server.status,
|
|
332
343
|
tools: (server.tools ?? []).map((tool) => {
|
|
333
344
|
const outputSchema = parseOutputSchemaJson(tool.outputSchemaJson);
|
|
345
|
+
const annotations = parseAnnotationsJson(tool.annotationsJson);
|
|
334
346
|
return {
|
|
335
347
|
name: tool.name,
|
|
336
348
|
description: tool.description ?? "",
|
|
337
349
|
inputSchema: normalizeInputSchema(tool.inputSchema),
|
|
338
350
|
...(outputSchema === undefined ? {} : { outputSchema }),
|
|
351
|
+
...(annotations === undefined ? {} : { annotations }),
|
|
339
352
|
};
|
|
340
353
|
}),
|
|
341
354
|
}));
|
|
@@ -507,6 +520,10 @@ export class CursorBackendClient {
|
|
|
507
520
|
return parseRpcResponse(method, second);
|
|
508
521
|
}
|
|
509
522
|
|
|
523
|
+
/**
|
|
524
|
+
* Serve a cached access token for the account, exchanging the API key
|
|
525
|
+
* when none is cached or it is near expiry.
|
|
526
|
+
*/
|
|
510
527
|
private getAccessToken(): Promise<string> {
|
|
511
528
|
if (
|
|
512
529
|
this.token !== undefined &&
|
|
@@ -703,6 +720,26 @@ function parseOutputSchemaJson(
|
|
|
703
720
|
: undefined;
|
|
704
721
|
}
|
|
705
722
|
|
|
723
|
+
/**
|
|
724
|
+
* Parse the flat JSON-string annotations off the wire and bound them to the
|
|
725
|
+
* MCP-spec fields. Malformed or non-object payloads behave like absent ones,
|
|
726
|
+
* so readers fail closed.
|
|
727
|
+
*/
|
|
728
|
+
function parseAnnotationsJson(
|
|
729
|
+
annotationsJson: string | undefined
|
|
730
|
+
): McpToolAnnotations | undefined {
|
|
731
|
+
if (annotationsJson === undefined) {
|
|
732
|
+
return undefined;
|
|
733
|
+
}
|
|
734
|
+
let parsed: unknown;
|
|
735
|
+
try {
|
|
736
|
+
parsed = JSON.parse(annotationsJson);
|
|
737
|
+
} catch {
|
|
738
|
+
return undefined;
|
|
739
|
+
}
|
|
740
|
+
return boundedToolAnnotations(parsed);
|
|
741
|
+
}
|
|
742
|
+
|
|
706
743
|
type McpResultJson = z.infer<typeof mcpResultSchema>;
|
|
707
744
|
|
|
708
745
|
function toCallResult(result: McpResultJson): AccountMcpCallResult {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Thin client for the Agent Serve deployment management API
|
|
3
|
-
* (`/internal/agent-serve/deployments` on the Cursor backend)
|
|
3
|
+
* (`/internal/agent-serve/deployments` on the Cursor backend) and the v2
|
|
4
|
+
* AgentSDK accept path (`/internal/agentkit/deployments`). Pure
|
|
4
5
|
* request/response + error mapping — no console I/O, no polling loops;
|
|
5
6
|
* rendering lives in `cli-deploy.ts`.
|
|
6
7
|
*
|
|
@@ -212,6 +213,48 @@ export interface StopAccepted {
|
|
|
212
213
|
|
|
213
214
|
export type DeleteAccepted = StopAccepted;
|
|
214
215
|
|
|
216
|
+
/**
|
|
217
|
+
* `POST /internal/agentkit/deployments` 202 body. Distinct from v1
|
|
218
|
+
* {@link DeployAccepted}: the v2 control plane returns catalog ids, not
|
|
219
|
+
* an engine alias or one-shot alias token.
|
|
220
|
+
*/
|
|
221
|
+
export interface AgentkitDeployAccepted {
|
|
222
|
+
applicationId: string;
|
|
223
|
+
releaseId: string;
|
|
224
|
+
status: string;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Customer-visible v2 deploy status (`GET /internal/agentkit/deployments/:slug`). */
|
|
228
|
+
export type AgentkitCustomerStatus =
|
|
229
|
+
| "pending"
|
|
230
|
+
| "deploying"
|
|
231
|
+
| "running"
|
|
232
|
+
| "failed"
|
|
233
|
+
| "stopped";
|
|
234
|
+
|
|
235
|
+
export type AgentkitDeployProgress =
|
|
236
|
+
| "queued"
|
|
237
|
+
| "creating"
|
|
238
|
+
| "building"
|
|
239
|
+
| "starting"
|
|
240
|
+
| "finalizing"
|
|
241
|
+
| "running"
|
|
242
|
+
| "failed"
|
|
243
|
+
| "stopped";
|
|
244
|
+
|
|
245
|
+
export interface AgentkitDeployStatus {
|
|
246
|
+
applicationId: string;
|
|
247
|
+
releaseId: string;
|
|
248
|
+
slug: string;
|
|
249
|
+
teamId: number;
|
|
250
|
+
status: AgentkitCustomerStatus;
|
|
251
|
+
progress: AgentkitDeployProgress;
|
|
252
|
+
statusMessage: string | null;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export const TERMINAL_AGENTKIT_DEPLOY_STATUSES: ReadonlySet<AgentkitCustomerStatus> =
|
|
256
|
+
new Set(["running", "failed", "stopped"]);
|
|
257
|
+
|
|
215
258
|
/** One secret's metadata from the list endpoint — names only, never values. */
|
|
216
259
|
export interface SecretInfo {
|
|
217
260
|
name: string;
|
|
@@ -260,6 +303,35 @@ const deployAcceptedSchema = z
|
|
|
260
303
|
})
|
|
261
304
|
.passthrough();
|
|
262
305
|
|
|
306
|
+
const agentkitDeployAcceptedSchema = z
|
|
307
|
+
.object({
|
|
308
|
+
applicationId: z.string().min(1),
|
|
309
|
+
releaseId: z.string().min(1),
|
|
310
|
+
status: z.string(),
|
|
311
|
+
})
|
|
312
|
+
.passthrough();
|
|
313
|
+
|
|
314
|
+
const agentkitDeployStatusSchema = z
|
|
315
|
+
.object({
|
|
316
|
+
applicationId: z.string().min(1),
|
|
317
|
+
releaseId: z.string().min(1),
|
|
318
|
+
slug: z.string().min(1),
|
|
319
|
+
teamId: z.number(),
|
|
320
|
+
status: z.enum(["pending", "deploying", "running", "failed", "stopped"]),
|
|
321
|
+
progress: z.enum([
|
|
322
|
+
"queued",
|
|
323
|
+
"creating",
|
|
324
|
+
"building",
|
|
325
|
+
"starting",
|
|
326
|
+
"finalizing",
|
|
327
|
+
"running",
|
|
328
|
+
"failed",
|
|
329
|
+
"stopped",
|
|
330
|
+
]),
|
|
331
|
+
statusMessage: z.string().nullable(),
|
|
332
|
+
})
|
|
333
|
+
.passthrough();
|
|
334
|
+
|
|
263
335
|
const deploymentSchema = z.object({
|
|
264
336
|
id: z.number(),
|
|
265
337
|
slug: z.string(),
|
|
@@ -482,6 +554,54 @@ export class AgentServeDeployClient {
|
|
|
482
554
|
return deployAcceptedSchema.parse(raw);
|
|
483
555
|
}
|
|
484
556
|
|
|
557
|
+
/**
|
|
558
|
+
* Create or redeploy onto the v2 control plane
|
|
559
|
+
* (`POST /internal/agentkit/deployments`). 202 means accepted; poll
|
|
560
|
+
* {@link getAgentkitDeploy} until a terminal status.
|
|
561
|
+
*/
|
|
562
|
+
async deployAgentkit(args: {
|
|
563
|
+
teamId: number;
|
|
564
|
+
slug: string;
|
|
565
|
+
gitRepoUrl: string;
|
|
566
|
+
gitRef?: string;
|
|
567
|
+
agentPath?: string;
|
|
568
|
+
repositories?: string[];
|
|
569
|
+
}): Promise<AgentkitDeployAccepted> {
|
|
570
|
+
const slugError = validateDeploymentSlug(args.slug);
|
|
571
|
+
if (slugError !== undefined) {
|
|
572
|
+
throw new DeployApiError(slugError, 400);
|
|
573
|
+
}
|
|
574
|
+
const raw = await this.request({
|
|
575
|
+
method: "POST",
|
|
576
|
+
path: "/internal/agentkit/deployments",
|
|
577
|
+
body: {
|
|
578
|
+
teamId: args.teamId,
|
|
579
|
+
slug: args.slug,
|
|
580
|
+
gitRepoUrl: args.gitRepoUrl,
|
|
581
|
+
gitRef: args.gitRef,
|
|
582
|
+
agentPath: args.agentPath,
|
|
583
|
+
repositories:
|
|
584
|
+
args.repositories !== undefined && args.repositories.length > 0
|
|
585
|
+
? args.repositories
|
|
586
|
+
: undefined,
|
|
587
|
+
},
|
|
588
|
+
context: { verb: "deploy", slug: args.slug },
|
|
589
|
+
});
|
|
590
|
+
return agentkitDeployAcceptedSchema.parse(raw);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
async getAgentkitDeploy(args: {
|
|
594
|
+
teamId: number;
|
|
595
|
+
slug: string;
|
|
596
|
+
}): Promise<AgentkitDeployStatus> {
|
|
597
|
+
const raw = await this.request({
|
|
598
|
+
method: "GET",
|
|
599
|
+
path: `/internal/agentkit/deployments/${encodeURIComponent(args.slug)}?teamId=${args.teamId}`,
|
|
600
|
+
context: { verb: "get deployment", slug: args.slug },
|
|
601
|
+
});
|
|
602
|
+
return agentkitDeployStatusSchema.parse(raw);
|
|
603
|
+
}
|
|
604
|
+
|
|
485
605
|
async listDeployments(args: { teamId: number }): Promise<Deployment[]> {
|
|
486
606
|
const raw = await this.request({
|
|
487
607
|
method: "GET",
|
|
@@ -756,7 +876,7 @@ function mapErrorResponse(
|
|
|
756
876
|
// (api2 vs api.cursor.com), not a missing deployment / closed gate.
|
|
757
877
|
if (/Route \S+ not found/i.test(bodyText)) {
|
|
758
878
|
return new DeployApiError(
|
|
759
|
-
`Agent Serve deploy API not found on this host — use the default api.cursor.com (unset CURSOR_API_BASE_URL), or point CURSOR_API_BASE_URL at a backend that mounts /internal/agent-serve/*.`,
|
|
879
|
+
`Agent Serve deploy API not found on this host — use the default api.cursor.com (unset CURSOR_API_BASE_URL), or point CURSOR_API_BASE_URL at a backend that mounts /internal/agent-serve/* and /internal/agentkit/*.`,
|
|
760
880
|
404
|
|
761
881
|
);
|
|
762
882
|
}
|