@playwo/opencode-cursor-oauth 0.0.0-dev.762b07a81479 → 0.0.0-dev.7fe465ca080f

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.
@@ -1,7 +1,7 @@
1
1
  import { create, toBinary } from "@bufbuild/protobuf";
2
- import { AgentClientMessageSchema, ClientHeartbeatSchema, ConversationStateStructureSchema, BackgroundShellSpawnResultSchema, DeleteResultSchema, DeleteRejectedSchema, DiagnosticsResultSchema, ExecClientMessageSchema, FetchErrorSchema, FetchResultSchema, GetBlobResultSchema, GrepErrorSchema, GrepResultSchema, KvClientMessageSchema, LsRejectedSchema, LsResultSchema, McpResultSchema, ReadRejectedSchema, ReadResultSchema, RequestContextResultSchema, RequestContextSchema, RequestContextSuccessSchema, SetBlobResultSchema, ShellRejectedSchema, ShellResultSchema, WriteRejectedSchema, WriteResultSchema, WriteShellStdinErrorSchema, WriteShellStdinResultSchema, } from "../proto/agent_pb";
2
+ import { AgentClientMessageSchema, AskQuestionInteractionResponseSchema, AskQuestionRejectedSchema, AskQuestionResultSchema, ClientHeartbeatSchema, ConversationStateStructureSchema, BackgroundShellSpawnResultSchema, CreatePlanErrorSchema, CreatePlanRequestResponseSchema, CreatePlanResultSchema, DeleteResultSchema, DeleteRejectedSchema, DiagnosticsResultSchema, ExecClientMessageSchema, ExaFetchRequestResponseSchema, ExaFetchRequestResponse_RejectedSchema, ExaSearchRequestResponseSchema, ExaSearchRequestResponse_RejectedSchema, FetchErrorSchema, FetchResultSchema, GetBlobResultSchema, GrepErrorSchema, GrepResultSchema, InteractionResponseSchema, KvClientMessageSchema, LsRejectedSchema, LsResultSchema, McpResultSchema, ReadRejectedSchema, ReadResultSchema, RequestContextResultSchema, RequestContextSchema, RequestContextSuccessSchema, SetBlobResultSchema, ShellRejectedSchema, ShellResultSchema, SwitchModeRequestResponseSchema, SwitchModeRequestResponse_RejectedSchema, WebSearchRequestResponseSchema, WebSearchRequestResponse_RejectedSchema, WriteRejectedSchema, WriteResultSchema, WriteShellStdinErrorSchema, WriteShellStdinResultSchema, } from "../proto/agent_pb";
3
3
  import { CONNECT_END_STREAM_FLAG } from "../cursor/config";
4
- import { logPluginError, logPluginWarn } from "../logger";
4
+ import { logPluginError, logPluginInfo, logPluginWarn } from "../logger";
5
5
  import { decodeMcpArgsMap } from "../openai/tools";
6
6
  export function parseConnectEndStream(data) {
7
7
  try {
@@ -128,8 +128,90 @@ export function computeUsage(state) {
128
128
  const prompt_tokens = Math.max(0, total_tokens - completion_tokens);
129
129
  return { prompt_tokens, completion_tokens, total_tokens };
130
130
  }
131
+ function replacePendingExec(state, exec) {
132
+ const existingIndex = state.pendingExecs.findIndex((candidate) => candidate.toolCallId === exec.toolCallId);
133
+ if (existingIndex >= 0) {
134
+ state.pendingExecs[existingIndex] = exec;
135
+ return;
136
+ }
137
+ state.pendingExecs.push(exec);
138
+ }
139
+ function hasUsableDecodedArgs(decodedArgs) {
140
+ const trimmed = decodedArgs.trim();
141
+ return trimmed !== "" && trimmed !== "{}";
142
+ }
143
+ function mergePendingExec(existing, incoming) {
144
+ const incomingHasExecMetadata = incoming.execMsgId !== 0;
145
+ const existingHasExecMetadata = existing.execMsgId !== 0;
146
+ return {
147
+ execId: incomingHasExecMetadata || !existing.execId ? incoming.execId : existing.execId,
148
+ execMsgId: incomingHasExecMetadata || !existingHasExecMetadata
149
+ ? incoming.execMsgId
150
+ : existing.execMsgId,
151
+ toolCallId: existing.toolCallId || incoming.toolCallId,
152
+ toolName: incoming.toolName && incoming.toolName !== "unknown_mcp_tool"
153
+ ? incoming.toolName
154
+ : existing.toolName,
155
+ decodedArgs: hasUsableDecodedArgs(incoming.decodedArgs)
156
+ ? incoming.decodedArgs
157
+ : existing.decodedArgs,
158
+ source: incomingHasExecMetadata ? incoming.source : existing.source ?? incoming.source,
159
+ cursorCallId: existing.cursorCallId || incoming.cursorCallId,
160
+ modelCallId: existing.modelCallId || incoming.modelCallId,
161
+ };
162
+ }
163
+ function emitPendingExec(exec, state, onMcpExec) {
164
+ const existing = state.pendingExecs.find((candidate) => candidate.toolCallId === exec.toolCallId);
165
+ const nextExec = existing ? mergePendingExec(existing, exec) : exec;
166
+ const hadActionableMetadata = (existing?.execMsgId ?? 0) !== 0;
167
+ const hasActionableMetadata = nextExec.execMsgId !== 0;
168
+ if (state.emittedToolCallIds.has(nextExec.toolCallId)) {
169
+ replacePendingExec(state, nextExec);
170
+ if (!hadActionableMetadata && hasActionableMetadata) {
171
+ logPluginInfo("Cursor MCP tool call metadata upgraded", {
172
+ toolCallId: nextExec.toolCallId,
173
+ toolName: nextExec.toolName,
174
+ source: nextExec.source,
175
+ execId: nextExec.execId,
176
+ execMsgId: nextExec.execMsgId,
177
+ cursorCallId: nextExec.cursorCallId,
178
+ modelCallId: nextExec.modelCallId,
179
+ });
180
+ }
181
+ else {
182
+ logPluginInfo("Ignored duplicate Cursor MCP tool call event", {
183
+ toolCallId: nextExec.toolCallId,
184
+ toolName: nextExec.toolName,
185
+ source: nextExec.source,
186
+ execId: nextExec.execId,
187
+ execMsgId: nextExec.execMsgId,
188
+ cursorCallId: nextExec.cursorCallId,
189
+ modelCallId: nextExec.modelCallId,
190
+ });
191
+ }
192
+ return;
193
+ }
194
+ state.emittedToolCallIds.add(nextExec.toolCallId);
195
+ replacePendingExec(state, nextExec);
196
+ logPluginInfo("Emitting Cursor MCP tool call", {
197
+ toolCallId: nextExec.toolCallId,
198
+ toolName: nextExec.toolName,
199
+ source: nextExec.source,
200
+ execId: nextExec.execId,
201
+ execMsgId: nextExec.execMsgId,
202
+ cursorCallId: nextExec.cursorCallId,
203
+ modelCallId: nextExec.modelCallId,
204
+ decodedArgs: nextExec.decodedArgs,
205
+ });
206
+ onMcpExec(nextExec);
207
+ }
131
208
  export function processServerMessage(msg, blobStore, mcpTools, sendFrame, state, onText, onMcpExec, onCheckpoint, onTurnEnded, onUnsupportedMessage, onUnhandledExec) {
132
209
  const msgCase = msg.message.case;
210
+ if (msgCase !== "conversationCheckpointUpdate") {
211
+ logPluginInfo("Received Cursor server message", {
212
+ messageCase: msgCase ?? "undefined",
213
+ });
214
+ }
133
215
  if (msgCase === "interactionUpdate") {
134
216
  handleInteractionUpdate(msg.message.value, state, onText, onMcpExec, onTurnEnded, onUnsupportedMessage);
135
217
  }
@@ -137,7 +219,7 @@ export function processServerMessage(msg, blobStore, mcpTools, sendFrame, state,
137
219
  handleKvMessage(msg.message.value, blobStore, sendFrame);
138
220
  }
139
221
  else if (msgCase === "execServerMessage") {
140
- handleExecMessage(msg.message.value, mcpTools, sendFrame, onMcpExec, onUnhandledExec);
222
+ handleExecMessage(msg.message.value, mcpTools, sendFrame, state, onMcpExec, onUnhandledExec);
141
223
  }
142
224
  else if (msgCase === "execServerControlMessage") {
143
225
  onUnsupportedMessage?.({
@@ -146,10 +228,7 @@ export function processServerMessage(msg, blobStore, mcpTools, sendFrame, state,
146
228
  });
147
229
  }
148
230
  else if (msgCase === "interactionQuery") {
149
- onUnsupportedMessage?.({
150
- category: "interactionQuery",
151
- caseName: msg.message.value.query.case ?? "undefined",
152
- });
231
+ handleInteractionQuery(msg.message.value, sendFrame, onUnsupportedMessage);
153
232
  }
154
233
  else if (msgCase === "conversationCheckpointUpdate") {
155
234
  const stateStructure = msg.message.value;
@@ -169,6 +248,16 @@ export function processServerMessage(msg, blobStore, mcpTools, sendFrame, state,
169
248
  }
170
249
  function handleInteractionUpdate(update, state, onText, onMcpExec, onTurnEnded, onUnsupportedMessage) {
171
250
  const updateCase = update.message?.case;
251
+ if (updateCase !== "textDelta" &&
252
+ updateCase !== "thinkingDelta" &&
253
+ updateCase !== "tokenDelta") {
254
+ logPluginInfo("Received Cursor interaction update", {
255
+ updateCase: updateCase ?? "undefined",
256
+ callId: update.message?.value?.callId,
257
+ modelCallId: update.message?.value?.modelCallId,
258
+ toolCase: update.message?.value?.toolCall?.tool?.case,
259
+ });
260
+ }
172
261
  if (updateCase === "textDelta") {
173
262
  const delta = update.message.value.text || "";
174
263
  if (delta)
@@ -185,19 +274,24 @@ function handleInteractionUpdate(update, state, onText, onMcpExec, onTurnEnded,
185
274
  else if (updateCase === "partialToolCall") {
186
275
  const partial = update.message.value;
187
276
  if (partial.callId && partial.argsTextDelta) {
188
- state.interactionToolArgsText.set(partial.callId, partial.argsTextDelta);
277
+ const existing = state.interactionToolArgsText.get(partial.callId) ?? "";
278
+ state.interactionToolArgsText.set(partial.callId, `${existing}${partial.argsTextDelta}`);
189
279
  }
190
280
  }
191
281
  else if (updateCase === "toolCallCompleted") {
192
282
  const exec = decodeInteractionToolCall(update.message.value, state);
193
- if (exec)
194
- onMcpExec(exec);
195
- else {
196
- onUnsupportedMessage?.({
197
- category: "toolCall",
198
- caseName: update.message.value?.toolCall?.tool?.case ?? "undefined",
199
- detail: "toolCallCompleted",
283
+ if (exec) {
284
+ logPluginInfo("Received Cursor interaction MCP tool call", {
285
+ toolCallId: exec.toolCallId,
286
+ toolName: exec.toolName,
287
+ source: exec.source,
288
+ execId: exec.execId,
289
+ execMsgId: exec.execMsgId,
290
+ cursorCallId: exec.cursorCallId,
291
+ modelCallId: exec.modelCallId,
292
+ decodedArgs: exec.decodedArgs,
200
293
  });
294
+ emitPendingExec(exec, state, onMcpExec);
201
295
  }
202
296
  }
203
297
  else if (updateCase === "turnEnded") {
@@ -221,9 +315,10 @@ function handleInteractionUpdate(update, state, onText, onMcpExec, onTurnEnded,
221
315
  caseName: updateCase ?? "undefined",
222
316
  });
223
317
  }
224
- // toolCallStarted, partialToolCall, toolCallDelta, toolCallCompleted
225
- // are intentionally ignored. MCP tool calls flow through the exec
226
- // message path (mcpArgs mcpResult), not interaction updates.
318
+ // toolCallStarted, partialToolCall, toolCallDelta, and non-MCP
319
+ // toolCallCompleted updates are informational only. Actionable MCP tool
320
+ // calls may still appear here on some models, so we surface those, but we
321
+ // do not abort the bridge for native Cursor tool-call progress events.
227
322
  }
228
323
  function decodeInteractionToolCall(update, state) {
229
324
  const callId = update.callId ?? "";
@@ -234,8 +329,6 @@ function decodeInteractionToolCall(update, state) {
234
329
  if (!mcpArgs)
235
330
  return null;
236
331
  const toolCallId = mcpArgs.toolCallId || callId || crypto.randomUUID();
237
- if (state.emittedToolCallIds.has(toolCallId))
238
- return null;
239
332
  const decodedMap = decodeMcpArgsMap(mcpArgs.args ?? {});
240
333
  const partialArgsText = callId
241
334
  ? state.interactionToolArgsText.get(callId)?.trim()
@@ -247,7 +340,6 @@ function decodeInteractionToolCall(update, state) {
247
340
  else if (partialArgsText) {
248
341
  decodedArgs = partialArgsText;
249
342
  }
250
- state.emittedToolCallIds.add(toolCallId);
251
343
  if (callId)
252
344
  state.interactionToolArgsText.delete(callId);
253
345
  return {
@@ -256,8 +348,95 @@ function decodeInteractionToolCall(update, state) {
256
348
  toolCallId,
257
349
  toolName: mcpArgs.toolName || mcpArgs.name || "unknown_mcp_tool",
258
350
  decodedArgs,
351
+ source: "interaction",
352
+ cursorCallId: callId || undefined,
353
+ modelCallId: update.modelCallId,
259
354
  };
260
355
  }
356
+ function handleInteractionQuery(query, sendFrame, onUnsupportedMessage) {
357
+ const queryCase = query.query.case;
358
+ if (queryCase === "webSearchRequestQuery") {
359
+ const response = create(WebSearchRequestResponseSchema, {
360
+ result: {
361
+ case: "rejected",
362
+ value: create(WebSearchRequestResponse_RejectedSchema, {
363
+ reason: "Native Cursor web search is not available in this environment. Use the provided MCP tool `websearch` instead.",
364
+ }),
365
+ },
366
+ });
367
+ sendInteractionResponse(query.id, "webSearchRequestResponse", response, sendFrame);
368
+ return;
369
+ }
370
+ if (queryCase === "askQuestionInteractionQuery") {
371
+ const response = create(AskQuestionInteractionResponseSchema, {
372
+ result: create(AskQuestionResultSchema, {
373
+ result: {
374
+ case: "rejected",
375
+ value: create(AskQuestionRejectedSchema, {
376
+ reason: "Native Cursor question prompts are not available in this environment. Use the provided MCP tool `question` instead.",
377
+ }),
378
+ },
379
+ }),
380
+ });
381
+ sendInteractionResponse(query.id, "askQuestionInteractionResponse", response, sendFrame);
382
+ return;
383
+ }
384
+ if (queryCase === "switchModeRequestQuery") {
385
+ const response = create(SwitchModeRequestResponseSchema, {
386
+ result: {
387
+ case: "rejected",
388
+ value: create(SwitchModeRequestResponse_RejectedSchema, {
389
+ reason: "Cursor mode switching is not available in this environment. Continue using the current agent and the provided MCP tools.",
390
+ }),
391
+ },
392
+ });
393
+ sendInteractionResponse(query.id, "switchModeRequestResponse", response, sendFrame);
394
+ return;
395
+ }
396
+ if (queryCase === "exaSearchRequestQuery") {
397
+ const response = create(ExaSearchRequestResponseSchema, {
398
+ result: {
399
+ case: "rejected",
400
+ value: create(ExaSearchRequestResponse_RejectedSchema, {
401
+ reason: "Native Cursor Exa search is not available in this environment. Use the provided MCP tool `websearch` instead.",
402
+ }),
403
+ },
404
+ });
405
+ sendInteractionResponse(query.id, "exaSearchRequestResponse", response, sendFrame);
406
+ return;
407
+ }
408
+ if (queryCase === "exaFetchRequestQuery") {
409
+ const response = create(ExaFetchRequestResponseSchema, {
410
+ result: {
411
+ case: "rejected",
412
+ value: create(ExaFetchRequestResponse_RejectedSchema, {
413
+ reason: "Native Cursor Exa fetch is not available in this environment. Use the provided MCP tools `websearch` and `webfetch` instead.",
414
+ }),
415
+ },
416
+ });
417
+ sendInteractionResponse(query.id, "exaFetchRequestResponse", response, sendFrame);
418
+ return;
419
+ }
420
+ if (queryCase === "createPlanRequestQuery") {
421
+ const response = create(CreatePlanRequestResponseSchema, {
422
+ result: create(CreatePlanResultSchema, {
423
+ planUri: "",
424
+ result: {
425
+ case: "error",
426
+ value: create(CreatePlanErrorSchema, {
427
+ error: "Native Cursor plan creation is not available in this environment. Use the provided MCP planning tools instead.",
428
+ }),
429
+ },
430
+ }),
431
+ });
432
+ sendInteractionResponse(query.id, "createPlanRequestResponse", response, sendFrame);
433
+ return;
434
+ }
435
+ onUnsupportedMessage?.({
436
+ category: "interactionQuery",
437
+ caseName: queryCase ?? "undefined",
438
+ });
439
+ }
261
440
  /** Send a KV client response back to Cursor. */
262
441
  function sendKvResponse(kvMsg, messageCase, value, sendFrame) {
263
442
  const response = create(KvClientMessageSchema, {
@@ -269,6 +448,16 @@ function sendKvResponse(kvMsg, messageCase, value, sendFrame) {
269
448
  });
270
449
  sendFrame(toBinary(AgentClientMessageSchema, clientMsg));
271
450
  }
451
+ function sendInteractionResponse(queryId, messageCase, value, sendFrame) {
452
+ const response = create(InteractionResponseSchema, {
453
+ id: queryId,
454
+ result: { case: messageCase, value: value },
455
+ });
456
+ const clientMessage = create(AgentClientMessageSchema, {
457
+ message: { case: "interactionResponse", value: response },
458
+ });
459
+ sendFrame(toBinary(AgentClientMessageSchema, clientMessage));
460
+ }
272
461
  function handleKvMessage(kvMsg, blobStore, sendFrame) {
273
462
  const kvCase = kvMsg.message.case;
274
463
  if (kvCase === "getBlobArgs") {
@@ -289,9 +478,19 @@ function handleKvMessage(kvMsg, blobStore, sendFrame) {
289
478
  sendKvResponse(kvMsg, "setBlobResult", create(SetBlobResultSchema, {}), sendFrame);
290
479
  }
291
480
  }
292
- function handleExecMessage(execMsg, mcpTools, sendFrame, onMcpExec, onUnhandledExec) {
481
+ function handleExecMessage(execMsg, mcpTools, sendFrame, state, onMcpExec, onUnhandledExec) {
293
482
  const execCase = execMsg.message.case;
483
+ logPluginInfo("Received Cursor exec message", {
484
+ execCase: execCase ?? "undefined",
485
+ execId: execMsg.execId,
486
+ execMsgId: execMsg.id,
487
+ });
294
488
  if (execCase === "requestContextArgs") {
489
+ logPluginInfo("Responding to Cursor requestContextArgs", {
490
+ execId: execMsg.execId,
491
+ execMsgId: execMsg.id,
492
+ mcpToolCount: mcpTools.length,
493
+ });
295
494
  const requestContext = create(RequestContextSchema, {
296
495
  rules: [],
297
496
  repositoryInfo: [],
@@ -314,13 +513,23 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, onMcpExec, onUnhandledE
314
513
  if (execCase === "mcpArgs") {
315
514
  const mcpArgs = execMsg.message.value;
316
515
  const decoded = decodeMcpArgsMap(mcpArgs.args ?? {});
317
- onMcpExec({
516
+ const exec = {
318
517
  execId: execMsg.execId,
319
518
  execMsgId: execMsg.id,
320
519
  toolCallId: mcpArgs.toolCallId || crypto.randomUUID(),
321
520
  toolName: mcpArgs.toolName || mcpArgs.name,
322
521
  decodedArgs: JSON.stringify(decoded),
522
+ source: "exec",
523
+ };
524
+ logPluginInfo("Received Cursor exec MCP tool metadata", {
525
+ toolCallId: exec.toolCallId,
526
+ toolName: exec.toolName,
527
+ source: exec.source,
528
+ execId: exec.execId,
529
+ execMsgId: exec.execMsgId,
530
+ decodedArgs: exec.decodedArgs,
323
531
  });
532
+ emitPendingExec(exec, state, onMcpExec);
324
533
  return;
325
534
  }
326
535
  // --- Reject native Cursor tools ---
@@ -328,6 +537,11 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, onMcpExec, onUnhandledE
328
537
  // so it falls back to our MCP tools (registered via RequestContext).
329
538
  const REJECT_REASON = "Tool not available in this environment. Use the MCP tools provided instead.";
330
539
  if (execCase === "readArgs") {
540
+ logPluginInfo("Rejecting native Cursor read tool in favor of MCP", {
541
+ execId: execMsg.execId,
542
+ execMsgId: execMsg.id,
543
+ path: execMsg.message.value.path,
544
+ });
331
545
  const args = execMsg.message.value;
332
546
  const result = create(ReadResultSchema, {
333
547
  result: {
@@ -342,6 +556,11 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, onMcpExec, onUnhandledE
342
556
  return;
343
557
  }
344
558
  if (execCase === "lsArgs") {
559
+ logPluginInfo("Rejecting native Cursor ls tool in favor of MCP", {
560
+ execId: execMsg.execId,
561
+ execMsgId: execMsg.id,
562
+ path: execMsg.message.value.path,
563
+ });
345
564
  const args = execMsg.message.value;
346
565
  const result = create(LsResultSchema, {
347
566
  result: {
@@ -356,6 +575,10 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, onMcpExec, onUnhandledE
356
575
  return;
357
576
  }
358
577
  if (execCase === "grepArgs") {
578
+ logPluginInfo("Rejecting native Cursor grep tool in favor of MCP", {
579
+ execId: execMsg.execId,
580
+ execMsgId: execMsg.id,
581
+ });
359
582
  const result = create(GrepResultSchema, {
360
583
  result: {
361
584
  case: "error",
@@ -366,6 +589,11 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, onMcpExec, onUnhandledE
366
589
  return;
367
590
  }
368
591
  if (execCase === "writeArgs") {
592
+ logPluginInfo("Rejecting native Cursor write tool in favor of MCP", {
593
+ execId: execMsg.execId,
594
+ execMsgId: execMsg.id,
595
+ path: execMsg.message.value.path,
596
+ });
369
597
  const args = execMsg.message.value;
370
598
  const result = create(WriteResultSchema, {
371
599
  result: {
@@ -380,6 +608,11 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, onMcpExec, onUnhandledE
380
608
  return;
381
609
  }
382
610
  if (execCase === "deleteArgs") {
611
+ logPluginInfo("Rejecting native Cursor delete tool in favor of MCP", {
612
+ execId: execMsg.execId,
613
+ execMsgId: execMsg.id,
614
+ path: execMsg.message.value.path,
615
+ });
383
616
  const args = execMsg.message.value;
384
617
  const result = create(DeleteResultSchema, {
385
618
  result: {
@@ -394,6 +627,13 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, onMcpExec, onUnhandledE
394
627
  return;
395
628
  }
396
629
  if (execCase === "shellArgs" || execCase === "shellStreamArgs") {
630
+ logPluginInfo("Rejecting native Cursor shell tool in favor of MCP", {
631
+ execId: execMsg.execId,
632
+ execMsgId: execMsg.id,
633
+ command: execMsg.message.value.command ?? "",
634
+ workingDirectory: execMsg.message.value.workingDirectory ?? "",
635
+ execCase,
636
+ });
397
637
  const args = execMsg.message.value;
398
638
  const result = create(ShellResultSchema, {
399
639
  result: {
@@ -410,6 +650,12 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, onMcpExec, onUnhandledE
410
650
  return;
411
651
  }
412
652
  if (execCase === "backgroundShellSpawnArgs") {
653
+ logPluginInfo("Rejecting native Cursor background shell tool in favor of MCP", {
654
+ execId: execMsg.execId,
655
+ execMsgId: execMsg.id,
656
+ command: execMsg.message.value.command ?? "",
657
+ workingDirectory: execMsg.message.value.workingDirectory ?? "",
658
+ });
413
659
  const args = execMsg.message.value;
414
660
  const result = create(BackgroundShellSpawnResultSchema, {
415
661
  result: {
@@ -426,6 +672,10 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, onMcpExec, onUnhandledE
426
672
  return;
427
673
  }
428
674
  if (execCase === "writeShellStdinArgs") {
675
+ logPluginInfo("Rejecting native Cursor shell stdin tool in favor of MCP", {
676
+ execId: execMsg.execId,
677
+ execMsgId: execMsg.id,
678
+ });
429
679
  const result = create(WriteShellStdinResultSchema, {
430
680
  result: {
431
681
  case: "error",
@@ -436,6 +686,11 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, onMcpExec, onUnhandledE
436
686
  return;
437
687
  }
438
688
  if (execCase === "fetchArgs") {
689
+ logPluginInfo("Rejecting native Cursor fetch tool in favor of MCP", {
690
+ execId: execMsg.execId,
691
+ execMsgId: execMsg.id,
692
+ url: execMsg.message.value.url,
693
+ });
439
694
  const args = execMsg.message.value;
440
695
  const result = create(FetchResultSchema, {
441
696
  result: {
@@ -450,6 +705,11 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, onMcpExec, onUnhandledE
450
705
  return;
451
706
  }
452
707
  if (execCase === "diagnosticsArgs") {
708
+ logPluginInfo("Rejecting native Cursor diagnostics tool in favor of MCP", {
709
+ execId: execMsg.execId,
710
+ execMsgId: execMsg.id,
711
+ path: execMsg.message.value.path,
712
+ });
453
713
  const result = create(DiagnosticsResultSchema, {});
454
714
  sendExecResult(execMsg, "diagnosticsResult", result, sendFrame);
455
715
  return;
@@ -463,6 +723,12 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, onMcpExec, onUnhandledE
463
723
  };
464
724
  const resultCase = miscCaseMap[execCase];
465
725
  if (resultCase) {
726
+ logPluginInfo("Responding to miscellaneous Cursor exec message", {
727
+ execCase,
728
+ execId: execMsg.execId,
729
+ execMsgId: execMsg.id,
730
+ resultCase,
731
+ });
466
732
  sendExecResult(execMsg, resultCase, create(McpResultSchema, {}), sendFrame);
467
733
  return;
468
734
  }
@@ -14,6 +14,9 @@ export interface PendingExec {
14
14
  toolName: string;
15
15
  /** Decoded arguments JSON string for SSE tool_calls emission. */
16
16
  decodedArgs: string;
17
+ source?: "interaction" | "exec";
18
+ cursorCallId?: string;
19
+ modelCallId?: string;
17
20
  }
18
21
  /** A live Cursor session kept alive across requests for tool result continuation. */
19
22
  export interface ActiveBridge {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playwo/opencode-cursor-oauth",
3
- "version": "0.0.0-dev.762b07a81479",
3
+ "version": "0.0.0-dev.7fe465ca080f",
4
4
  "description": "OpenCode plugin that connects Cursor's API to OpenCode via OAuth, model discovery, and a local OpenAI-compatible proxy.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -19,7 +19,6 @@
19
19
  ],
20
20
  "scripts": {
21
21
  "build": "tsc -p tsconfig.json",
22
- "test": "bun test/smoke.ts",
23
22
  "prepublishOnly": "npm run build"
24
23
  },
25
24
  "repository": {