@cursor/july 0.1.9 → 0.1.11
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 +1 -0
- package/dist/bin/agent-serve.js +26 -3
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +2 -1
- package/dist/channels/github/types.d.ts +4 -0
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/slack/cursor-account.d.ts.map +1 -1
- package/dist/channels/slack/cursor-account.js +1 -1
- package/dist/channels/slack/dispatch.d.ts +13 -0
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +36 -11
- package/dist/channels/slack/eval-directive.d.ts +63 -0
- package/dist/channels/slack/eval-directive.d.ts.map +1 -0
- package/dist/channels/slack/eval-directive.js +283 -0
- package/dist/channels/slack/index.d.ts +1 -0
- package/dist/channels/slack/index.d.ts.map +1 -1
- package/dist/channels/slack/index.js +1 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +4 -2
- package/dist/channels/slack/types.d.ts +7 -0
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/connections.d.ts +10 -5
- package/dist/connections.d.ts.map +1 -1
- package/dist/connections.js +10 -5
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.Ciosf24M.js → app.BcXUGca7.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BIbvK8M1.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.BcRDly7n.js → VPLocalSearchBox.CWur6XGZ.js} +1 -1
- package/dist/docs/assets/chunks/{theme.CMpbjdfx.js → theme.BnvulOX2.js} +2 -2
- package/dist/docs/assets/{deployment.md.DC5-ImcM.js → deployment.md.TecHo0_2.js} +3 -3
- package/dist/docs/assets/{deployment.md.DC5-ImcM.lean.js → deployment.md.TecHo0_2.lean.js} +1 -1
- package/dist/docs/assets/guides_mcp-oauth.md.DqmEOQY1.js +29 -0
- package/dist/docs/assets/guides_mcp-oauth.md.DqmEOQY1.lean.js +1 -0
- package/dist/docs/assets/{index.md.COiu-1jL.js → index.md.t0TM2Qzz.js} +3 -3
- package/dist/docs/assets/{index.md.COiu-1jL.lean.js → index.md.t0TM2Qzz.lean.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.D189RBCH.js → reference_cli.md.B74NrVzb.js} +5 -4
- package/dist/docs/assets/{reference_cli.md.D189RBCH.lean.js → reference_cli.md.B74NrVzb.lean.js} +1 -1
- package/dist/docs/assets/{reference_connections.md.zaEYCLHT.js → reference_connections.md.CsSr3rDz.js} +9 -4
- package/dist/docs/assets/{reference_connections.md.zaEYCLHT.lean.js → reference_connections.md.CsSr3rDz.lean.js} +1 -1
- package/dist/docs/assets/reference_prompt.md.BaiweQxE.js +1 -0
- package/dist/docs/assets/reference_prompt.md.BaiweQxE.lean.js +1 -0
- package/dist/docs/assets/{reference_tools.md.CqgJroI0.js → reference_tools.md.BRSDnTbN.js} +16 -3
- package/dist/docs/assets/{reference_tools.md.CqgJroI0.lean.js → reference_tools.md.BRSDnTbN.lean.js} +1 -1
- package/dist/docs/assets/{troubleshooting.md.DkovjS7h.js → troubleshooting.md.CmQkmnzC.js} +1 -1
- package/dist/docs/assets/{troubleshooting.md.DkovjS7h.lean.js → troubleshooting.md.CmQkmnzC.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +7 -7
- package/dist/docs/evals.html +4 -4
- package/dist/docs/example-agents/approval-buddy.html +4 -4
- package/dist/docs/example-agents/benny.html +4 -4
- package/dist/docs/example-agents/bugbot.html +4 -4
- package/dist/docs/example-agents/codebase-wiki.html +4 -4
- package/dist/docs/example-agents/codeowners-review.html +4 -4
- package/dist/docs/example-agents/concierge.html +4 -4
- package/dist/docs/example-agents/fsd.html +4 -4
- package/dist/docs/example-agents/index.html +4 -4
- package/dist/docs/example-agents/knowledge-base.html +4 -4
- package/dist/docs/example-agents/oncall.html +4 -4
- package/dist/docs/example-agents/security-reviewer.html +4 -4
- package/dist/docs/example-agents/slack-agent.html +4 -4
- package/dist/docs/example-agents/weather-agent.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +4 -4
- package/dist/docs/guides/github.html +4 -4
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +53 -0
- package/dist/docs/guides/slack.html +4 -4
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +6 -6
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +8 -7
- package/dist/docs/reference/connections.html +13 -8
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/prompt.html +33 -0
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +4 -4
- package/dist/docs/reference/skills.html +5 -5
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +20 -7
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/storage.html +4 -4
- package/dist/docs/troubleshooting.html +5 -5
- package/dist/evals.d.ts +39 -0
- package/dist/evals.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +7 -4
- package/dist/internal/cli-mcp-oauth.d.ts +16 -0
- package/dist/internal/cli-mcp-oauth.d.ts.map +1 -0
- package/dist/internal/cli-mcp-oauth.js +194 -0
- package/dist/internal/cli-mcp.d.ts +4 -1
- package/dist/internal/cli-mcp.d.ts.map +1 -1
- package/dist/internal/cli-mcp.js +19 -1
- package/dist/internal/corp-ca.d.ts +9 -0
- package/dist/internal/corp-ca.d.ts.map +1 -0
- package/dist/internal/corp-ca.js +44 -0
- package/dist/internal/cursor/account-mcp.d.ts +17 -4
- package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
- package/dist/internal/cursor/account-mcp.js +105 -17
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +64 -10
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +1 -0
- package/dist/internal/logs-client.js +1 -1
- package/dist/internal/mcp-host.d.ts.map +1 -1
- package/dist/internal/mcp-host.js +49 -26
- package/dist/internal/mcp-oauth.d.ts +88 -0
- package/dist/internal/mcp-oauth.d.ts.map +1 -0
- package/dist/internal/mcp-oauth.js +385 -0
- package/dist/internal/resolved-connections.d.ts +6 -0
- package/dist/internal/resolved-connections.d.ts.map +1 -1
- package/dist/internal/resolved-connections.js +10 -3
- package/dist/internal/sdk-runner.d.ts +9 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +28 -2
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +24 -6
- package/dist/internal/session-engine.d.ts +30 -10
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +166 -74
- package/dist/internal/sessions-client.d.ts +16 -2
- package/dist/internal/sessions-client.d.ts.map +1 -1
- package/dist/internal/sessions-client.js +30 -13
- package/dist/internal/storage-coordinator.d.ts +11 -0
- package/dist/internal/storage-coordinator.d.ts.map +1 -1
- package/dist/internal/storage-coordinator.js +30 -0
- package/dist/internal/stream-progress.d.ts.map +1 -1
- package/dist/internal/stream-progress.js +8 -4
- package/dist/internal/turn-governor.d.ts +54 -0
- package/dist/internal/turn-governor.d.ts.map +1 -0
- package/dist/internal/turn-governor.js +111 -0
- package/dist/playground/assets/{index-C0-QEG4O.js → index-BIGLubwK.js} +46 -46
- package/dist/playground/assets/{index-z3JY9O2x.css → index-C17ZQKDG.css} +1 -1
- package/dist/playground/index.html +2 -2
- package/dist/prompt.d.ts +39 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +70 -0
- package/dist/storage-backends/cursor-hosted.d.ts +10 -0
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/storage-backends/cursor-hosted.js +14 -0
- package/dist/types.d.ts +93 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +6 -0
- package/docs/README.md +4 -0
- package/docs/deployment.md +6 -1
- package/docs/guides/mcp-oauth.md +157 -0
- package/docs/reference/cli.md +30 -0
- package/docs/reference/connections.md +32 -2
- package/docs/reference/prompt.md +47 -0
- package/docs/reference/tools.md +21 -0
- package/docs/troubleshooting.md +11 -0
- package/package.json +9 -2
- package/skills/create-agent/SKILL.md +7 -3
- package/skills/framework-map/SKILL.md +1 -0
- package/skills/mcp-auth/SKILL.md +138 -0
- package/src/bin/agent-serve.ts +30 -3
- package/src/channels/github/github-channel.ts +2 -0
- package/src/channels/github/types.ts +4 -0
- package/src/channels/slack/cursor-account.ts +2 -0
- package/src/channels/slack/dispatch.ts +49 -0
- package/src/channels/slack/eval-directive.ts +348 -0
- package/src/channels/slack/index.ts +1 -0
- package/src/channels/slack/slack-channel.ts +3 -0
- package/src/channels/slack/types.ts +7 -0
- package/src/connections.ts +10 -5
- package/src/evals.ts +43 -0
- package/src/index.ts +3 -0
- package/src/internal/cli-ax.ts +10 -4
- package/src/internal/cli-mcp-oauth.ts +254 -0
- package/src/internal/cli-mcp.ts +25 -2
- package/src/internal/cursor/account-mcp.ts +153 -41
- package/src/internal/discovery.ts +70 -8
- package/src/internal/distribution.ts +1 -0
- package/src/internal/logs-client.ts +1 -1
- package/src/internal/mcp-host.ts +33 -9
- package/src/internal/mcp-oauth.ts +496 -0
- package/src/internal/resolved-connections.ts +20 -5
- package/src/internal/sdk-runner.ts +37 -1
- package/src/internal/server.ts +28 -6
- package/src/internal/session-engine.ts +192 -76
- package/src/internal/sessions-client.ts +51 -15
- package/src/internal/storage-coordinator.ts +36 -0
- package/src/internal/stream-progress.ts +8 -0
- package/src/internal/turn-governor.ts +145 -0
- package/src/prompt.ts +83 -0
- package/src/storage-backends/cursor-hosted.ts +21 -0
- package/src/types.ts +92 -6
- package/dist/docs/assets/chunks/@localSearchIndexroot.D154Detj.js +0 -1
package/src/evals.ts
CHANGED
|
@@ -315,6 +315,49 @@ export interface EvalRunSnapshot {
|
|
|
315
315
|
};
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
+
/** One discovered eval datapoint as returned by {@link HostEvalsApi.list}. */
|
|
319
|
+
export interface HostEvalListItem {
|
|
320
|
+
id: string;
|
|
321
|
+
fileId: string;
|
|
322
|
+
description?: string;
|
|
323
|
+
tags?: string[];
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** Config slice returned with {@link HostEvalsApi.list}. */
|
|
327
|
+
export interface HostEvalListConfig {
|
|
328
|
+
maxConcurrency?: number;
|
|
329
|
+
timeoutMs?: number;
|
|
330
|
+
maxPlaygroundRuns: number;
|
|
331
|
+
/** True when `agent/storage.ts` declares an `evals` table. */
|
|
332
|
+
durableRuns: boolean;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** Input for {@link HostEvalsApi.start} (same filters as `POST /v1/dev/evals/runs`). */
|
|
336
|
+
export interface HostStartEvalRunInput {
|
|
337
|
+
filterIds?: string[];
|
|
338
|
+
tags?: string[];
|
|
339
|
+
/** Per-case timeout override (same semantics as CLI `--timeout-ms`). */
|
|
340
|
+
timeoutMs?: number;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Host-facing playground eval batches — the same surface as
|
|
345
|
+
* `/v1/dev/evals*` and `agentkit eval --prod`. Attached to
|
|
346
|
+
* {@link import("./types.js").HostContext.evals} by `serve`.
|
|
347
|
+
*/
|
|
348
|
+
export interface HostEvalsApi {
|
|
349
|
+
list(): Promise<{
|
|
350
|
+
evals: HostEvalListItem[];
|
|
351
|
+
config: HostEvalListConfig;
|
|
352
|
+
}>;
|
|
353
|
+
get(runId: string): Promise<EvalRunSnapshot | undefined>;
|
|
354
|
+
listRunsWithActive(): Promise<{
|
|
355
|
+
runs: EvalRunSnapshot[];
|
|
356
|
+
activeRunId?: string;
|
|
357
|
+
}>;
|
|
358
|
+
start(input?: HostStartEvalRunInput): Promise<EvalRunSnapshot>;
|
|
359
|
+
}
|
|
360
|
+
|
|
318
361
|
export interface EvalConfigFile {
|
|
319
362
|
timeoutMs?: number;
|
|
320
363
|
/**
|
package/src/index.ts
CHANGED
|
@@ -89,6 +89,8 @@ export {
|
|
|
89
89
|
UnknownSessionError,
|
|
90
90
|
UnknownToolError,
|
|
91
91
|
} from "./internal/session-engine.js";
|
|
92
|
+
export type { PromptTag } from "./prompt.js";
|
|
93
|
+
export { prompt } from "./prompt.js";
|
|
92
94
|
export type {
|
|
93
95
|
ReminderBusyPolicy,
|
|
94
96
|
ReminderCreateInput,
|
|
@@ -127,6 +129,7 @@ export {
|
|
|
127
129
|
isCursorAccountTransport,
|
|
128
130
|
isPeerConnectionTransport,
|
|
129
131
|
isSymbolicConnectionTransport,
|
|
132
|
+
MAX_RUNNING_TURNS_LIMIT,
|
|
130
133
|
} from "./types.js";
|
|
131
134
|
|
|
132
135
|
/**
|
package/src/internal/cli-ax.ts
CHANGED
|
@@ -54,7 +54,7 @@ import { runSession } from "./run-client.js";
|
|
|
54
54
|
import {
|
|
55
55
|
fetchSessionEvents,
|
|
56
56
|
formatSessionsTable,
|
|
57
|
-
|
|
57
|
+
listSessionsWithSource,
|
|
58
58
|
resolvePreviousSessionId,
|
|
59
59
|
} from "./sessions-client.js";
|
|
60
60
|
import { createStreamProgress } from "./stream-progress.js";
|
|
@@ -1383,7 +1383,7 @@ export async function cmdSessions(options: AxCliOptions): Promise<number> {
|
|
|
1383
1383
|
return target;
|
|
1384
1384
|
}
|
|
1385
1385
|
try {
|
|
1386
|
-
const
|
|
1386
|
+
const listed = await listSessionsWithSource({
|
|
1387
1387
|
baseUrl: target.agentUrl,
|
|
1388
1388
|
auth: {
|
|
1389
1389
|
bearerToken: options.bearerToken,
|
|
@@ -1392,10 +1392,16 @@ export async function cmdSessions(options: AxCliOptions): Promise<number> {
|
|
|
1392
1392
|
fetchImpl: options.fetchImpl,
|
|
1393
1393
|
});
|
|
1394
1394
|
if (options.json === true) {
|
|
1395
|
-
|
|
1395
|
+
const payload =
|
|
1396
|
+
listed.source === undefined
|
|
1397
|
+
? { sessions: listed.sessions }
|
|
1398
|
+
: { sessions: listed.sessions, source: listed.source };
|
|
1399
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
1396
1400
|
return 0;
|
|
1397
1401
|
}
|
|
1398
|
-
process.stdout.write(
|
|
1402
|
+
process.stdout.write(
|
|
1403
|
+
formatSessionsTable(listed.sessions, { source: listed.source })
|
|
1404
|
+
);
|
|
1399
1405
|
return 0;
|
|
1400
1406
|
} catch (error) {
|
|
1401
1407
|
process.stderr.write(
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agentkit mcp oauth <connection> [--store]` for `defineConnection({ url, oauth: true })`.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
6
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
7
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
8
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
9
|
+
import type { OAuthTokens } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
10
|
+
import { resolveDeployAuth } from "./cli-deploy.js";
|
|
11
|
+
import { loadAgentProject } from "./discovery.js";
|
|
12
|
+
import { CLI_COMMAND_NAME as CLI, LOG_PREFIX } from "./distribution.js";
|
|
13
|
+
import {
|
|
14
|
+
createHostMcpOAuthProvider,
|
|
15
|
+
MCP_OAUTH_CALLBACK_URL,
|
|
16
|
+
mcpOauthSecretNames,
|
|
17
|
+
openBrowser,
|
|
18
|
+
waitForMcpOAuthCallback,
|
|
19
|
+
} from "./mcp-oauth.js";
|
|
20
|
+
import { resolveProdSlug } from "./resolve-prod-target.js";
|
|
21
|
+
|
|
22
|
+
export type McpOauthCliOptions = {
|
|
23
|
+
dir: string;
|
|
24
|
+
connection: string;
|
|
25
|
+
/** Upsert MCP_OAUTH_* on the hosted deployment. */
|
|
26
|
+
store?: boolean;
|
|
27
|
+
slug?: string;
|
|
28
|
+
team?: string;
|
|
29
|
+
apiKey?: string;
|
|
30
|
+
err?: (text: string) => void;
|
|
31
|
+
out?: (text: string) => void;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function describe(error: unknown): string {
|
|
35
|
+
if (!(error instanceof Error)) {
|
|
36
|
+
return String(error);
|
|
37
|
+
}
|
|
38
|
+
const cause = error.cause;
|
|
39
|
+
if (!(cause instanceof Error)) {
|
|
40
|
+
return error.message;
|
|
41
|
+
}
|
|
42
|
+
const code =
|
|
43
|
+
"code" in cause && typeof cause.code === "string" ? cause.code : undefined;
|
|
44
|
+
const detail =
|
|
45
|
+
code === undefined ? cause.message : `${cause.message} (${code})`;
|
|
46
|
+
return `${error.message}: ${detail}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function cmdMcpOauth(
|
|
50
|
+
options: McpOauthCliOptions
|
|
51
|
+
): Promise<number> {
|
|
52
|
+
const err = options.err ?? ((text) => process.stderr.write(text));
|
|
53
|
+
const out = options.out ?? ((text) => process.stdout.write(text));
|
|
54
|
+
const project = await loadAgentProject(options.dir);
|
|
55
|
+
const connection = project.agent.connections.find(
|
|
56
|
+
(entry) => entry.name === options.connection
|
|
57
|
+
);
|
|
58
|
+
if (connection === undefined) {
|
|
59
|
+
const known =
|
|
60
|
+
project.agent.connections.map((entry) => entry.name).join(", ") ||
|
|
61
|
+
"(none)";
|
|
62
|
+
err(
|
|
63
|
+
`${LOG_PREFIX} Unknown MCP connection "${options.connection}". Authored: ${known}.\n`
|
|
64
|
+
);
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const transport = connection.transport;
|
|
69
|
+
if (!("url" in transport) || transport.oauth !== true) {
|
|
70
|
+
err(
|
|
71
|
+
`${LOG_PREFIX} "${options.connection}" must be defineConnection({ url, oauth: true }).\n`
|
|
72
|
+
);
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
out(
|
|
77
|
+
`${LOG_PREFIX} Authorizing "${options.connection}" (${transport.url})…\n`
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
let authorizationUrl: URL | undefined;
|
|
81
|
+
const provider = await createHostMcpOAuthProvider({
|
|
82
|
+
identifier: options.connection,
|
|
83
|
+
resourceUrl: transport.url,
|
|
84
|
+
onRedirect: (url) => {
|
|
85
|
+
authorizationUrl = url;
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const client = new Client({ name: "agentkit-mcp-oauth", version: "0.1.0" });
|
|
90
|
+
const url = new URL(transport.url);
|
|
91
|
+
const requestInit =
|
|
92
|
+
transport.headers === undefined
|
|
93
|
+
? undefined
|
|
94
|
+
: { headers: transport.headers };
|
|
95
|
+
|
|
96
|
+
const makeTransport = ():
|
|
97
|
+
| StreamableHTTPClientTransport
|
|
98
|
+
| SSEClientTransport => {
|
|
99
|
+
if (transport.type === "sse") {
|
|
100
|
+
return new SSEClientTransport(url, {
|
|
101
|
+
authProvider: provider,
|
|
102
|
+
requestInit,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return new StreamableHTTPClientTransport(url, {
|
|
106
|
+
authProvider: provider,
|
|
107
|
+
requestInit,
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
let mcpTransport = makeTransport();
|
|
112
|
+
let authorized = false;
|
|
113
|
+
try {
|
|
114
|
+
await client.connect(mcpTransport);
|
|
115
|
+
out(`${LOG_PREFIX} "${options.connection}" already authorized.\n`);
|
|
116
|
+
await client.close().catch(() => undefined);
|
|
117
|
+
authorized = true;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (!(error instanceof UnauthorizedError)) {
|
|
120
|
+
err(
|
|
121
|
+
`${LOG_PREFIX} Failed to connect to "${options.connection}": ${describe(error)}\n`
|
|
122
|
+
);
|
|
123
|
+
return 1;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!authorized) {
|
|
128
|
+
const redirect = authorizationUrl ?? provider.lastRedirectUrl;
|
|
129
|
+
if (redirect === undefined) {
|
|
130
|
+
err(
|
|
131
|
+
`${LOG_PREFIX} Auth required but no authorization URL was provided.\n`
|
|
132
|
+
);
|
|
133
|
+
return 1;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
out(`${LOG_PREFIX} Open if needed:\n ${redirect.toString()}\n`);
|
|
137
|
+
out(`${LOG_PREFIX} Waiting on ${MCP_OAUTH_CALLBACK_URL}…\n`);
|
|
138
|
+
openBrowser(redirect.toString());
|
|
139
|
+
|
|
140
|
+
let callback: Awaited<ReturnType<typeof waitForMcpOAuthCallback>>;
|
|
141
|
+
try {
|
|
142
|
+
callback = await waitForMcpOAuthCallback();
|
|
143
|
+
} catch (error) {
|
|
144
|
+
err(`${LOG_PREFIX} OAuth callback failed: ${describe(error)}\n`);
|
|
145
|
+
return 1;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
await mcpTransport.finishAuth(callback.code);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
err(`${LOG_PREFIX} Token exchange failed: ${describe(error)}\n`);
|
|
152
|
+
return 1;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
mcpTransport = makeTransport();
|
|
156
|
+
try {
|
|
157
|
+
await client.connect(mcpTransport);
|
|
158
|
+
const tools = await client.listTools();
|
|
159
|
+
out(
|
|
160
|
+
`${LOG_PREFIX} Authorized "${options.connection}" (${tools.tools.length} tools). Local tokens in the agent-serve config dir.\n`
|
|
161
|
+
);
|
|
162
|
+
await client.close().catch(() => undefined);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
err(
|
|
165
|
+
`${LOG_PREFIX} Reconnect failed: ${describe(error)}. Re-run \`${CLI} mcp oauth ${options.connection}\`.\n`
|
|
166
|
+
);
|
|
167
|
+
return 1;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (options.store !== true) {
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
|
174
|
+
return storeOauthSecrets({
|
|
175
|
+
connection: options.connection,
|
|
176
|
+
dir: options.dir,
|
|
177
|
+
slug: options.slug,
|
|
178
|
+
team: options.team,
|
|
179
|
+
apiKey: options.apiKey,
|
|
180
|
+
tokens: provider.tokens(),
|
|
181
|
+
clientId: provider.clientInformation()?.client_id,
|
|
182
|
+
err,
|
|
183
|
+
out,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function storeOauthSecrets(args: {
|
|
188
|
+
connection: string;
|
|
189
|
+
dir: string;
|
|
190
|
+
slug?: string;
|
|
191
|
+
team?: string;
|
|
192
|
+
apiKey?: string;
|
|
193
|
+
tokens: OAuthTokens | undefined;
|
|
194
|
+
clientId: string | undefined;
|
|
195
|
+
err: (text: string) => void;
|
|
196
|
+
out: (text: string) => void;
|
|
197
|
+
}): Promise<number> {
|
|
198
|
+
const tokens = args.tokens;
|
|
199
|
+
if (tokens === undefined || tokens.access_token.trim() === "") {
|
|
200
|
+
args.err(
|
|
201
|
+
`${LOG_PREFIX} No access token to store for "${args.connection}".\n`
|
|
202
|
+
);
|
|
203
|
+
return 1;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
let slug: string;
|
|
207
|
+
try {
|
|
208
|
+
slug = resolveProdSlug({ dir: args.dir, slug: args.slug });
|
|
209
|
+
} catch (error) {
|
|
210
|
+
args.err(`${LOG_PREFIX} ${describe(error)}\n`);
|
|
211
|
+
return 1;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const auth = await resolveDeployAuth({
|
|
215
|
+
team: args.team,
|
|
216
|
+
apiKey: args.apiKey,
|
|
217
|
+
err: args.err,
|
|
218
|
+
});
|
|
219
|
+
if (typeof auth === "number") {
|
|
220
|
+
return auth;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const names = mcpOauthSecretNames(args.connection);
|
|
224
|
+
const secrets: Array<{ name: string; value: string }> = [
|
|
225
|
+
{ name: names.accessToken, value: tokens.access_token },
|
|
226
|
+
];
|
|
227
|
+
if (
|
|
228
|
+
tokens.refresh_token !== undefined &&
|
|
229
|
+
tokens.refresh_token.trim() !== ""
|
|
230
|
+
) {
|
|
231
|
+
secrets.push({ name: names.refreshToken, value: tokens.refresh_token });
|
|
232
|
+
}
|
|
233
|
+
if (args.clientId !== undefined && args.clientId.trim() !== "") {
|
|
234
|
+
secrets.push({ name: names.clientId, value: args.clientId });
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const secretNames = await auth.client.setSecrets({
|
|
239
|
+
teamId: auth.teamId,
|
|
240
|
+
slug,
|
|
241
|
+
secrets,
|
|
242
|
+
});
|
|
243
|
+
args.out(
|
|
244
|
+
`${LOG_PREFIX} Stored on ${slug}: ${secrets.map((s) => s.name).join(", ")}.\n` +
|
|
245
|
+
`${LOG_PREFIX} Secrets now: ${secretNames.join(", ")}. Next deploy picks them up.\n`
|
|
246
|
+
);
|
|
247
|
+
return 0;
|
|
248
|
+
} catch (error) {
|
|
249
|
+
args.err(
|
|
250
|
+
`${LOG_PREFIX} Failed to store secrets on ${slug}: ${describe(error)}\n`
|
|
251
|
+
);
|
|
252
|
+
return 1;
|
|
253
|
+
}
|
|
254
|
+
}
|
package/src/internal/cli-mcp.ts
CHANGED
|
@@ -89,7 +89,10 @@ export interface McpCliOptions {
|
|
|
89
89
|
|
|
90
90
|
export async function cmdMcp(
|
|
91
91
|
subcommand: string | undefined,
|
|
92
|
-
options: McpCliOptions
|
|
92
|
+
options: McpCliOptions & {
|
|
93
|
+
oauthConnection?: string;
|
|
94
|
+
store?: boolean;
|
|
95
|
+
}
|
|
93
96
|
): Promise<number> {
|
|
94
97
|
const err = options.err ?? defaultErr;
|
|
95
98
|
if (subcommand === undefined) {
|
|
@@ -98,9 +101,29 @@ export async function cmdMcp(
|
|
|
98
101
|
if (subcommand === "install") {
|
|
99
102
|
return cmdMcpInstall(options);
|
|
100
103
|
}
|
|
104
|
+
if (subcommand === "oauth") {
|
|
105
|
+
const connection = options.oauthConnection;
|
|
106
|
+
if (connection === undefined || connection.trim() === "") {
|
|
107
|
+
err(
|
|
108
|
+
`Usage: ${CLI} mcp oauth <connection> [--dir .] [--store] [--slug <slug>] [--team <id>]\n` +
|
|
109
|
+
`Authorize defineConnection({ url, oauth: true }) (host PKCE).\n` +
|
|
110
|
+
`--store upserts MCP_OAUTH_<NAME>_* secrets on the hosted deployment.\n`
|
|
111
|
+
);
|
|
112
|
+
return 1;
|
|
113
|
+
}
|
|
114
|
+
const { cmdMcpOauth } = await import("./cli-mcp-oauth.js");
|
|
115
|
+
return cmdMcpOauth({
|
|
116
|
+
dir: options.dir,
|
|
117
|
+
connection,
|
|
118
|
+
store: options.store,
|
|
119
|
+
slug: options.slug,
|
|
120
|
+
team: options.team,
|
|
121
|
+
apiKey: options.apiKey,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
101
124
|
err(
|
|
102
125
|
`Unknown mcp subcommand "${subcommand}".\n` +
|
|
103
|
-
`Usage: ${CLI} mcp [install] --prod [--slug <slug>] | --url <baseUrl>\n`
|
|
126
|
+
`Usage: ${CLI} mcp [install|oauth <connection>] --prod [--slug <slug>] | --url <baseUrl>\n`
|
|
104
127
|
);
|
|
105
128
|
return 1;
|
|
106
129
|
}
|
|
@@ -213,48 +213,89 @@ export class CursorAccountMcpBridge {
|
|
|
213
213
|
|
|
214
214
|
/**
|
|
215
215
|
* Startup DX: log the account's connector statuses for `refs` without
|
|
216
|
-
* blocking serve startup. Surfaces "needsAuth" connectors
|
|
217
|
-
*
|
|
216
|
+
* blocking serve startup. Surfaces "needsAuth" / "error" connectors with
|
|
217
|
+
* remediation hints (and a backend probe detail for `error`), and filters
|
|
218
|
+
* that matched nothing (typo'd connector names).
|
|
219
|
+
*
|
|
220
|
+
* Returns a promise that settles when every ref's log lines are written —
|
|
221
|
+
* callers may ignore it; tests can await it.
|
|
218
222
|
*/
|
|
219
|
-
logPreflight(refs: CursorAccountConnectionRef[]): void {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if (needsAuth.length > 0) {
|
|
244
|
-
this.logger(
|
|
245
|
-
`[agentkit] Cursor account MCP connection "${ref.name}": ${needsAuth
|
|
246
|
-
.map((server) => server.serverIdentifier)
|
|
247
|
-
.join(
|
|
248
|
-
", "
|
|
249
|
-
)} need re-authorization in Cursor before their tools appear`
|
|
223
|
+
logPreflight(refs: CursorAccountConnectionRef[]): Promise<void> {
|
|
224
|
+
return Promise.all(refs.map((ref) => this.runPreflight(ref))).then(
|
|
225
|
+
() => undefined
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private async runPreflight(ref: CursorAccountConnectionRef): Promise<void> {
|
|
230
|
+
try {
|
|
231
|
+
const catalog = await this.getCatalog(ref);
|
|
232
|
+
if (catalog.servers.length === 0) {
|
|
233
|
+
this.logger(
|
|
234
|
+
`[agentkit] Cursor account MCP connection "${ref.name}": no matching connectors on this account — connect servers in Cursor (dashboard → MCP) or fix the \`servers\` filter`
|
|
235
|
+
);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
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
|
|
250
247
|
);
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
248
|
+
if (detail !== undefined) {
|
|
249
|
+
errorDetails.set(server.serverIdentifier, detail);
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
const summary = catalog.servers
|
|
255
|
+
.map((server) =>
|
|
256
|
+
formatServerStatusSummary(
|
|
257
|
+
server,
|
|
258
|
+
errorDetails.get(server.serverIdentifier)
|
|
259
|
+
)
|
|
260
|
+
)
|
|
261
|
+
.join(", ");
|
|
262
|
+
this.logger(
|
|
263
|
+
`[agentkit] Cursor account MCP connection "${ref.name}": ${summary}`
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
const needsAuth = catalog.servers.filter(
|
|
267
|
+
(server) => server.status === "needsAuth"
|
|
268
|
+
);
|
|
269
|
+
if (needsAuth.length > 0) {
|
|
270
|
+
this.logger(
|
|
271
|
+
`[agentkit] 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
|
+
`[agentkit] Cursor account MCP connection "${ref.name}": ${details} — Cursor backend could not use this connector (not a missing agentkit 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
|
+
} catch (error: unknown) {
|
|
296
|
+
this.logger(
|
|
297
|
+
`[agentkit] Cursor account MCP connection "${ref.name}" preflight failed: ${describeError(error)}`
|
|
298
|
+
);
|
|
258
299
|
}
|
|
259
300
|
}
|
|
260
301
|
|
|
@@ -283,13 +324,22 @@ export class CursorAccountMcpBridge {
|
|
|
283
324
|
const tool = await this.findTool(ref, request.params.name);
|
|
284
325
|
if (tool === undefined) {
|
|
285
326
|
const catalog = await this.getCatalog(ref);
|
|
327
|
+
const unhealthy = catalog.servers.filter(
|
|
328
|
+
(server) => server.status !== "connected"
|
|
329
|
+
);
|
|
330
|
+
const statusHint =
|
|
331
|
+
unhealthy.length === 0
|
|
332
|
+
? ""
|
|
333
|
+
: ` Unhealthy connectors: ${unhealthy
|
|
334
|
+
.map((server) => `${server.serverIdentifier}=${server.status}`)
|
|
335
|
+
.join(", ")}.`;
|
|
286
336
|
return {
|
|
287
337
|
content: [
|
|
288
338
|
{
|
|
289
339
|
type: "text",
|
|
290
340
|
text: `Unknown tool "${request.params.name}". Available: ${
|
|
291
341
|
catalog.tools.map((t) => t.exposedName).join(", ") || "(none)"
|
|
292
|
-
}
|
|
342
|
+
}.${statusHint}`,
|
|
293
343
|
},
|
|
294
344
|
],
|
|
295
345
|
isError: true,
|
|
@@ -470,3 +520,65 @@ function trimTrailingSlash(url: string): string {
|
|
|
470
520
|
function describeError(error: unknown): string {
|
|
471
521
|
return error instanceof Error ? error.message : String(error);
|
|
472
522
|
}
|
|
523
|
+
|
|
524
|
+
/** One-line status for the preflight summary (`name (12 tools)` / `name (error: …)`). */
|
|
525
|
+
export function formatServerStatusSummary(
|
|
526
|
+
server: AccountMcpServer,
|
|
527
|
+
errorDetail?: string
|
|
528
|
+
): string {
|
|
529
|
+
switch (server.status) {
|
|
530
|
+
case "connected":
|
|
531
|
+
return `${server.serverIdentifier} (${server.tools.length} tools)`;
|
|
532
|
+
case "needsAuth":
|
|
533
|
+
return `${server.serverIdentifier} (needsAuth — re-authorize in Cursor)`;
|
|
534
|
+
case "loading":
|
|
535
|
+
return `${server.serverIdentifier} (loading)`;
|
|
536
|
+
case "error": {
|
|
537
|
+
const detail =
|
|
538
|
+
errorDetail === undefined || errorDetail.trim() === ""
|
|
539
|
+
? "backend could not reach connector"
|
|
540
|
+
: errorDetail.trim();
|
|
541
|
+
return `${server.serverIdentifier} (error: ${detail})`;
|
|
542
|
+
}
|
|
543
|
+
default:
|
|
544
|
+
return `${server.serverIdentifier} (${server.status})`;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Best-effort detail for an `error`-status connector. ListSandMcpTools only
|
|
550
|
+
* returns the status enum today, so probe ExecuteSandMcpTool (ping) and surface
|
|
551
|
+
* the backend failure text (e.g. `HTTP MCP tool execution failed: fetch failed`).
|
|
552
|
+
*/
|
|
553
|
+
export async function diagnoseConnectorError(
|
|
554
|
+
client: Pick<CursorBackendClient, "executeAccountMcpTool">,
|
|
555
|
+
serverIdentifier: string
|
|
556
|
+
): Promise<string | undefined> {
|
|
557
|
+
try {
|
|
558
|
+
const result = await client.executeAccountMcpTool({
|
|
559
|
+
serverIdentifier,
|
|
560
|
+
// Conventional health tool; even a tool-not-found path usually still
|
|
561
|
+
// surfaces the same transport failure when the connector is down.
|
|
562
|
+
toolName: `${serverIdentifier}-ping`,
|
|
563
|
+
args: {},
|
|
564
|
+
});
|
|
565
|
+
const text = result.content
|
|
566
|
+
.filter(
|
|
567
|
+
(part): part is { type: "text"; text: string } => part.type === "text"
|
|
568
|
+
)
|
|
569
|
+
.map((part) => part.text.trim())
|
|
570
|
+
.filter((part) => part.length > 0)
|
|
571
|
+
.join(" ");
|
|
572
|
+
if (result.isError) {
|
|
573
|
+
return text.length > 0
|
|
574
|
+
? text
|
|
575
|
+
: "backend reported an error with no detail";
|
|
576
|
+
}
|
|
577
|
+
// Listing said error but ping succeeded — surface that inconsistency.
|
|
578
|
+
return text.length > 0
|
|
579
|
+
? `listing reported error but probe succeeded: ${text}`
|
|
580
|
+
: "listing reported error but probe succeeded";
|
|
581
|
+
} catch (error: unknown) {
|
|
582
|
+
return describeError(error);
|
|
583
|
+
}
|
|
584
|
+
}
|