@magpie-community/opencode-cursor-auth 0.2.18 → 0.2.20
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/index.mjs +28 -13
- package/native-session.mjs +11 -2
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -1179,6 +1179,19 @@ function toolsOf(chat) {
|
|
|
1179
1179
|
})
|
|
1180
1180
|
}
|
|
1181
1181
|
|
|
1182
|
+
// NS is the MCP server the caller's tools are served as, the way Cursor's
|
|
1183
|
+
// own client names a user's server by its mcp.json label (providerIdentifier,
|
|
1184
|
+
// with "<label>-<tool>" as a call's name). It goes to Cursor in the prompt,
|
|
1185
|
+
// each call and each tool definition, so it names nothing of the plugin or
|
|
1186
|
+
// the app it runs in. Calls are read back under NAMESPACES: before 0.2.20
|
|
1187
|
+
// the server was named "magpie", and a session begun then can still call it.
|
|
1188
|
+
const NS = "tools"
|
|
1189
|
+
const NAMESPACES = [NS, "magpie"]
|
|
1190
|
+
const bareName = (s) => {
|
|
1191
|
+
for (const ns of NAMESPACES) if (s.startsWith(ns + "-")) return s.slice(ns.length + 1)
|
|
1192
|
+
return s
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1182
1195
|
// catalog lists the caller's tools for the model. It sees MCP tools only
|
|
1183
1196
|
// through Cursor's GetDynamicTools and CallDynamicTool, and Cursor names
|
|
1184
1197
|
// them in a prompt of its own, which this conversation goes without:
|
|
@@ -1188,8 +1201,8 @@ function toolsOf(chat) {
|
|
|
1188
1201
|
// those calls went nowhere (#11).
|
|
1189
1202
|
function catalog(tools) {
|
|
1190
1203
|
if (!tools.length) return ""
|
|
1191
|
-
let s = `\n\n<dynamic_tool_catalog>\nThe MCP namespace "
|
|
1192
|
-
for (const t of tools) s += `<call>${CALL}({"namespace":"
|
|
1204
|
+
let s = `\n\n<dynamic_tool_catalog>\nThe MCP namespace "${NS}" has the tools below. They are reached only through \`${CALL}\`, never as tools of their own. Each entry is the exact call to make; its arguments must match the schema given. The schemas are complete, so there is no need to call \`GetDynamicTools\` first.\n`
|
|
1205
|
+
for (const t of tools) s += `<call>${CALL}({"namespace":"${NS}","toolName":"${t.name}","arguments":{...}})\n${t.description}\narguments schema: ${t.schemaText}\n</call>\n`
|
|
1193
1206
|
return s + "</dynamic_tool_catalog>"
|
|
1194
1207
|
}
|
|
1195
1208
|
|
|
@@ -1263,7 +1276,7 @@ function conversation(chat, tools) {
|
|
|
1263
1276
|
if (c?.type && c.type !== "function") continue
|
|
1264
1277
|
const args = parseArgs(c.function?.arguments)
|
|
1265
1278
|
size += (c.function?.arguments ?? "").length + (c.function?.name ?? "").length
|
|
1266
|
-
content.push({ type: "tool-call", toolCallId: callID(c.id), toolName: CALL, args: { namespace:
|
|
1279
|
+
content.push({ type: "tool-call", toolCallId: callID(c.id), toolName: CALL, args: { namespace: NS, toolName: c.function?.name, arguments: args } })
|
|
1267
1280
|
pending.push(c.id)
|
|
1268
1281
|
}
|
|
1269
1282
|
if (content.length) add({ role: "assistant", content })
|
|
@@ -1303,7 +1316,7 @@ function toolDef(t) {
|
|
|
1303
1316
|
const d = pb().str(1, t.name)
|
|
1304
1317
|
if (t.description) d.str(2, t.description)
|
|
1305
1318
|
d.bytes(3, pbValue(t.schema))
|
|
1306
|
-
return d.str(4,
|
|
1319
|
+
return d.str(4, NS).str(5, t.name).str(6, t.schemaText).done()
|
|
1307
1320
|
}
|
|
1308
1321
|
|
|
1309
1322
|
const env = () => pb().str(1, GOOS).str(2, tmpdir()).str(10, "UTC")
|
|
@@ -2527,13 +2540,13 @@ function exec(msg, tools, send, closeExec, refused, native = false) {
|
|
|
2527
2540
|
if (native) for (const e of es) if (e.num === 11) {
|
|
2528
2541
|
if (e.wire !== 2) throw new NativeSessionError(502, "invalid native MCP argument envelope")
|
|
2529
2542
|
const { fields: a, args } = nativeMcpArgs(e.data)
|
|
2530
|
-
const name = pbStr(a, 5) || pbStr(a, 1)
|
|
2543
|
+
const name = pbStr(a, 5) || bareName(pbStr(a, 1))
|
|
2531
2544
|
const provider = pbStr(a, 4)
|
|
2532
2545
|
const server = pbStr(a, 9)
|
|
2533
|
-
if (provider && provider
|
|
2546
|
+
if (provider && !NAMESPACES.includes(provider) || server && !NAMESPACES.includes(server)) nativeUnavailable = true
|
|
2534
2547
|
if (pbNum(a, 7) || field(a, 6)) nativeUnavailable = true
|
|
2535
2548
|
if (!tools.some((t) => t.name === name)) nativeUnavailable = true
|
|
2536
|
-
if (!pbStr(a, 5) && pbStr(a, 1)
|
|
2549
|
+
if (!pbStr(a, 5) && !NAMESPACES.some((ns) => pbStr(a, 1) === ns + "-" + name)) nativeUnavailable = true
|
|
2537
2550
|
const call = { requestID: id, execID, nativeID: pbStr(a, 3), name, args }
|
|
2538
2551
|
if (nativeCall && !isDeepStrictEqual(nativeCall, call)) throw new NativeSessionError(502, "ambiguous repeated native MCP argument envelopes")
|
|
2539
2552
|
nativeCall ??= call
|
|
@@ -2565,7 +2578,7 @@ function exec(msg, tools, send, closeExec, refused, native = false) {
|
|
|
2565
2578
|
const v = field(ef, 2)
|
|
2566
2579
|
args[pbStr(ef, 1)] = v ? pbAny(v.data) : null
|
|
2567
2580
|
}
|
|
2568
|
-
const name = pbStr(a, 5) || pbStr(a, 1)
|
|
2581
|
+
const name = pbStr(a, 5) || bareName(pbStr(a, 1))
|
|
2569
2582
|
// an OpenAI model's id is its call's and its item's, a line apart,
|
|
2570
2583
|
// which no caller would take as an id: the line goes as __
|
|
2571
2584
|
const cid = pbStr(a, 3).replaceAll("\n", "__") || "call_" + randomBytes(12).toString("hex")
|
|
@@ -2573,7 +2586,7 @@ function exec(msg, tools, send, closeExec, refused, native = false) {
|
|
|
2573
2586
|
}
|
|
2574
2587
|
case 36: {
|
|
2575
2588
|
// mcp_state_exec_args: the tools there are
|
|
2576
|
-
const srv = pb().str(1,
|
|
2589
|
+
const srv = pb().str(1, NS).str(2, NS).str(7, "connected")
|
|
2577
2590
|
for (const t of tools) srv.bytes(5, toolDef(t))
|
|
2578
2591
|
answer(36, pb().bytes(1, pb().bytes(1, srv)))
|
|
2579
2592
|
return null
|
|
@@ -2656,12 +2669,12 @@ async function answerOf(auth, chat, signal, session = "") {
|
|
|
2656
2669
|
let response = native ? await runNative({ ...args, native }) : await runOnce(args)
|
|
2657
2670
|
const rebuild = async () => {
|
|
2658
2671
|
const fallback = await runOnce(args)
|
|
2659
|
-
fallback.diagnostic = "stateless; bootstrap=imported-history; reason=missing-batch-count"
|
|
2672
|
+
fallback.diagnostic = "stateless; bootstrap=imported-history; reason=" + (response.error?.continuityReason || "missing-batch-count")
|
|
2660
2673
|
return fallback
|
|
2661
2674
|
}
|
|
2662
|
-
// A missing native batch count may be retried once,
|
|
2663
|
-
// output is exposed or any native result was submitted.
|
|
2664
|
-
// uncertain tool
|
|
2675
|
+
// A missing or conflicting native batch count may be retried once,
|
|
2676
|
+
// only before Chat output is exposed or any native result was submitted.
|
|
2677
|
+
// Never repeat an uncertain tool write or progressive stream output.
|
|
2665
2678
|
if (response.error?.retentionIneligible && !signal?.aborted) response = await rebuild()
|
|
2666
2679
|
else if (native) response.rebuild = rebuild
|
|
2667
2680
|
response.diagnostic ??= "stateless; bootstrap=imported-history; reason=" + (prepared.fallback || "retention capacity or reservation unavailable")
|
|
@@ -2719,8 +2732,10 @@ async function answerOf(auth, chat, signal, session = "") {
|
|
|
2719
2732
|
for await (const e of events) {
|
|
2720
2733
|
if (e.error) {
|
|
2721
2734
|
if (!e.error.retentionIneligible || !r.rebuild || signal?.aborted) return errorResponse(e.error)
|
|
2735
|
+
const reason = e.error.continuityReason
|
|
2722
2736
|
r = await r.rebuild()
|
|
2723
2737
|
if (r.error) return errorResponse(r.error)
|
|
2738
|
+
if (reason) r.diagnostic = "stateless; bootstrap=imported-history; reason=" + reason
|
|
2724
2739
|
events = r.events
|
|
2725
2740
|
responseHeaders["X-Magpie-Cursor-Continuity"] = r.diagnostic
|
|
2726
2741
|
retry = true
|
package/native-session.mjs
CHANGED
|
@@ -675,7 +675,13 @@ class NativeSession {
|
|
|
675
675
|
}
|
|
676
676
|
listed(count) {
|
|
677
677
|
if (!Number.isSafeInteger(count) || count < 1) invalid("invalid native tool count", 502)
|
|
678
|
-
if (this.segment.expected != null && this.segment.expected !== count)
|
|
678
|
+
if (this.segment.expected != null && this.segment.expected !== count) {
|
|
679
|
+
// Conflicting counts leave the native step boundary ambiguous. Retire
|
|
680
|
+
// this owner; Chat may rebuild from history only if no native result
|
|
681
|
+
// was submitted to this Run.
|
|
682
|
+
this.fail({ status: 502, message: "inconsistent native tool count", retentionIneligible: !this.resultSubmitted, continuityReason: "inconsistent-batch-count" })
|
|
683
|
+
return
|
|
684
|
+
}
|
|
679
685
|
this.segment.expected = count
|
|
680
686
|
if (this.state === "awaiting-results") return // an identical retransmission
|
|
681
687
|
this.waitForBatch()
|
|
@@ -876,7 +882,10 @@ class NativeSession {
|
|
|
876
882
|
} else if (u.num === 14) {
|
|
877
883
|
if (!this.segment.content && !this.segment.calls.length) invalid("an empty reply (Cursor ended the turn without text or a tool call)", 502)
|
|
878
884
|
this.terminalSeen(uf)
|
|
879
|
-
} else if (u.num === 27)
|
|
885
|
+
} else if (u.num === 27) {
|
|
886
|
+
this.listed(num(uf, 1))
|
|
887
|
+
if (this.error) return
|
|
888
|
+
}
|
|
880
889
|
}
|
|
881
890
|
break
|
|
882
891
|
case 2: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magpie-community/opencode-cursor-auth",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.20",
|
|
4
4
|
"description": "Cursor subscriptions (Pro, Pro+, Ultra, Teams) in OpenCode and magpie, through the API cursor-agent talks to",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.mjs",
|