Microsoft Debug Adapter Protocol v1.66

Debug Adapter
Protocol Guide

A comprehensive visual reference to DAP—the JSON-based protocol that standardizes communication between development tools and debuggers. With Kotlin & Java examples.

RequestsResponsesEvents

What is the Debug Adapter Protocol?

How DAP eliminates the M x N debugger integration problem

The Debug Adapter Protocol (DAP) is a JSON-based protocol created by Microsoft that standardizes how development tools (IDEs and editors) communicate with debuggers.

Before DAP, every IDE needed a custom integration for every debugger—creating an M × N problem. With M editors and N debugger backends, you needed M×N integrations. DAP reduces this to M + N: each editor implements one DAP client, and each debugger provides one DAP adapter.

DAP uses a request-response pattern with asynchronous events. Messages are JSON objects transported over stdin/stdout or TCP sockets, framed with HTTP-style Content-Length headers.

Transport Framing

Content-Length: 119\r\n
\r\n
{
  "seq": 1,
  "type": "request",
  "command": "initialize",
  "arguments": {
    "clientID": "vscode",
    "adapterID": "kotlin"
  }
}

Each message is preceded by a header with Content-Length followed by CRLFCRLF, then the JSON payload.

Architecture

Client, Adapter, Debuggee: the three-actor model

Development Tool(IDE / Editor)Debug UI / ControlsDAP Client LibraryVS Code, Neovim, Emacs...Debug Adapter(Intermediary)Protocol TranslationDAP ↔ Native Debug APIkotlin-debug-adapter, java-debugDebuggee(Target Program)Application CodeJVM / RuntimeKotlin/Java ApplicationDAPJDWP/JDIJSON messagesover stdio/socketNative debug API(JVM Debug Interface)DAP ArchitectureThe three-actor model of the Debug Adapter ProtocolRequestResponseEventNative API

Client

The IDE or editor (VS Code, Neovim, IntelliJ). Provides debug UI for breakpoints, stepping, variable inspection. Sends DAP requests.

Debug Adapter

The intermediary that translates DAP into native debug APIs. For Kotlin/JVM: the kotlin-debug-adapter or java-debug server using JDWP.

Debuggee

The target program being debugged. Runs on the JVM with the debug agent enabled (-agentlib:jdwp). Controlled via the adapter.

Protocol Messages

Three message types: Request, Response, and Event

Requests are sent by the client to the adapter. Each request has a unique seq number, a command name, and optional arguments.

REQUESTRequest base structure
{
  "seq": 1,
  "type": "request",
  "command": "initialize",
  "arguments": {
    "clientID": "vscode",
    "clientName": "Visual Studio Code",
    "adapterID": "kotlin",
    "pathFormat": "path",
    "linesStartAt1": true,
    "columnsStartAt1": true,
    "supportsVariableType": true,
    "supportsVariablePaging": true,
    "supportsRunInTerminalRequest": true
  }
}

Debug Session Lifecycle

From initialization to disconnect — the complete session lifecycle

Complete DAP Session FlowClient (IDE)Debug AdapterinitializeNegotiate capabilitiesinitialize responseReturn adapter capabilitiesinitialized eventAdapter is readysetBreakpointsSet breakpoints in source filessetBreakpoints responsesetExceptionBreakpointssetExceptionBreakpoints responseconfigurationDoneAll configuration sentconfigurationDone responselaunchStart debuggeelaunch responseprocess eventProcess startedthread eventThread startedstopped eventHit breakpoint!stackTraceInspect call stackstackTrace responsescopesscopes responsevariablesGet variable valuesvariables responsecontinueResume executioncontinue responseterminated eventProgram finisheddisconnectdisconnect response

Session Initialization Flow

ClientAdapter
initialize request
initialize response (capabilities)
initialized event
setBreakpoints
setBreakpoints response
configurationDone
configurationDone response
launch
launch response
process event
thread event (started)

Lifecycle Phases

1. Initialize

Client and adapter negotiate capabilities

2. Configure

Set breakpoints, exception filters, function breakpoints

3. Run / Debug

Launch or attach, then step/continue through code

4. Terminate

Disconnect, clean up debuggee process

Request Reference

All major DAP request types with JSON examples

The first request sent by the client. Informs the adapter about client capabilities and receives adapter capabilities in return. Must be sent before any other request.

REQUESTinitialize request
{
  "seq": 1,
  "type": "request",
  "command": "initialize",
  "arguments": {
    "clientID": "vscode",
    "clientName": "Visual Studio Code",
    "adapterID": "kotlin",
    "locale": "en-us",
    "linesStartAt1": true,
    "columnsStartAt1": true,
    "pathFormat": "path",
    "supportsVariableType": true,
    "supportsVariablePaging": true,
    "supportsRunInTerminalRequest": true,
    "supportsMemoryReferences": true,
    "supportsInvalidatedEvent": true
  }
}

Asks the adapter to launch the debuggee. For Kotlin/JVM, this typically launches the JVM with debug instrumentation.

Kotlinmain.kt — The program to debug
1package com.example.debug
2
3class="kt-keyword">fun factorial(n: Int): Long {
4 class="kt-keyword">if (n <= class="kt-number">1) class="kt-keyword">return class="kt-number">1L
5 class="kt-keyword">return n * factorial(n - class="kt-number">1)
6}
7
8class="kt-keyword">fun main() {
9 class="kt-keyword">val number = class="kt-number">5
10 println(class="kt-string">"Computing factorial of $number")
11 class="kt-keyword">val result = factorial(number)
12 println(class="kt-string">"factorial($number) = $result")
13}
REQUESTlaunch request (Kotlin)
{
  "seq": 5,
  "type": "request",
  "command": "launch",
  "arguments": {
    "type": "kotlin",
    "name": "Launch Factorial",
    "request": "launch",
    "mainClass": "com.example.debug.MainKt",
    "projectRoot": "/home/dev/factorial-project",
    "classPaths": ["/home/dev/factorial-project/build/classes/kotlin/main"],
    "vmArguments": "-Xmx256m",
    "noDebug": false
  }
}

Attaches to an already-running debuggee. The target JVM must have the JDWP agent listening.

JVM launch command for remote debugging
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 -jar app.jar
REQUESTattach request
{
  "seq": 5,
  "type": "request",
  "command": "attach",
  "arguments": {
    "type": "kotlin",
    "hostName": "localhost",
    "port": 5005,
    "timeout": 10000
  }
}

Sets all breakpoints for a given source file. Replaces all previous breakpoints in that file.

REQUESTsetBreakpoints request
{
  "seq": 3,
  "type": "request",
  "command": "setBreakpoints",
  "arguments": {
    "source": {
      "name": "main.kt",
      "path": "/home/dev/project/src/main/kotlin/main.kt"
    },
    "breakpoints": [
      { "line": 4 },
      { "line": 11, "condition": "number > 3" },
      { "line": 12, "hitCondition": "3", "logMessage": "Result: {result}" }
    ],
    "sourceModified": false
  }
}
{
  "seq": 4,
  "type": "request",
  "command": "setFunctionBreakpoints",
  "arguments": {
    "breakpoints": [
      { "name": "factorial" },
      { "name": "main", "condition": "args.size > 0" }
    ]
  }
}

Configures which exceptions should cause the debugger to break. Filter IDs come from theexceptionBreakpointFilters capability.

{
  "seq": 4,
  "type": "request",
  "command": "setExceptionBreakpoints",
  "arguments": {
    "filters": ["uncaught"],
    "filterOptions": [
      { "filterId": "caught", "condition": "java.lang.ArithmeticException" }
    ]
  }
}

Sent after all breakpoints and configuration. Tells the adapter to start or resume the debuggee.

{ "seq": 6, "type": "request", "command": "configurationDone" }
RESPONSEthreads response
{
  "seq": 10,
  "type": "response",
  "request_seq": 9,
  "command": "threads",
  "success": true,
  "body": {
    "threads": [
      { "id": 1, "name": "main" },
      { "id": 2, "name": "Reference Handler" },
      { "id": 3, "name": "Finalizer" },
      { "id": 4, "name": "Signal Dispatcher" }
    ]
  }
}
{
  "seq": 12,
  "type": "request",
  "command": "stackTrace",
  "arguments": { "threadId": 1, "startFrame": 0, "levels": 20 }
}
RESPONSEscopes response
{
  "seq": 14,
  "type": "response",
  "request_seq": 13,
  "command": "scopes",
  "success": true,
  "body": {
    "scopes": [
      {
        "name": "Locals",
        "presentationHint": "locals",
        "variablesReference": 1001,
        "namedVariables": 2,
        "expensive": false
      }
    ]
  }
}

Retrieves all child variables for a given variablesReference.

{
  "seq": 16,
  "type": "response",
  "request_seq": 15,
  "command": "variables",
  "success": true,
  "body": {
    "variables": [
      { "name": "n", "value": "5", "type": "Int", "variablesReference": 0, "evaluateName": "n" },
      { "name": "this", "value": "MainKt", "type": "com.example.debug.MainKt", "variablesReference": 1002 }
    ]
  }
}

Evaluates an expression in the context of a stack frame. Used for debug console, hover tooltips, and watch expressions.

{
  "seq": 20,
  "type": "request",
  "command": "evaluate",
  "arguments": { "expression": "n * factorial(n - 1)", "frameId": 1, "context": "watch" }
}
continue

Resume until next breakpoint or end

next

Step over — execute current line, stop at next

stepIn

Step into — enter function calls

stepOut

Step out — run until current function returns

REQUESTnext (step over) request
{
  "seq": 25,
  "type": "request",
  "command": "next",
  "arguments": { "threadId": 1, "granularity": "statement" }
}
{ "seq": 30, "type": "request", "command": "pause", "arguments": { "threadId": 1 } }
{
  "seq": 35,
  "type": "request",
  "command": "setVariable",
  "arguments": { "variablesReference": 1001, "name": "n", "value": "10" }
}

Retrieves source code for a source reference. Used when source is not available locally (e.g., decompiled bytecode).

{
  "seq": 40,
  "type": "request",
  "command": "source",
  "arguments": { "source": { "sourceReference": 1000 }, "sourceReference": 1000 }
}

Ends the debug session. terminateDebuggee controls whether the process is also killed.

{
  "seq": 50,
  "type": "request",
  "command": "disconnect",
  "arguments": { "restart": false, "terminateDebuggee": true }
}
{ "seq": 48, "type": "request", "command": "terminate", "arguments": { "restart": false } }
{ "seq": 55, "type": "request", "command": "restart", "arguments": {} }
{
  "seq": 60,
  "type": "request",
  "command": "completions",
  "arguments": { "frameId": 1, "text": "fact", "column": 5 }
}
RESPONSEcompletions response
{
  "seq": 60,
  "type": "response",
  "request_seq": 60,
  "command": "completions",
  "success": true,
  "body": {
    "targets": [
      { "label": "factorial", "type": "function" },
      { "label": "factorialResult", "type": "variable" }
    ]
  }
}
{
  "seq": 45,
  "type": "response",
  "request_seq": 44,
  "command": "exceptionInfo",
  "success": true,
  "body": {
    "exceptionId": "java.lang.StackOverflowError",
    "description": "Stack overflow in recursive call",
    "breakMode": "always",
    "details": {
      "message": "Stack overflow in recursive call",
      "typeName": "java.lang.StackOverflowError",
      "stackTrace": "at com.example.debug.MainKt.factorial(main.kt: 5)\n..."
    }
  }
}

Events Reference

Asynchronous notifications from the adapter to the client

initialized

Adapter is ready for configuration requests

stopped

Execution stopped (breakpoint, step, exception, pause)

continued

Execution has resumed

exited

Debuggee has exited with an exit code

terminated

Debugging session is finished

thread

A thread has started or exited

output

Program output (stdout, stderr, console)

breakpoint

Breakpoint state changed (verified, moved)

module

Module (JAR, class file) loaded/changed

process

Debuggee process information

capabilities

Adapter capabilities have changed

loadedSource

Source file loaded, changed, or removed

The most important event. Fired when execution stops. The reason field indicates why.

EVENTstopped — breakpoint
{
  "seq": 15,
  "type": "event",
  "event": "stopped",
  "body": {
    "reason": "breakpoint",
    "description": "Paused on breakpoint",
    "threadId": 1,
    "allThreadsStopped": true,
    "hitBreakpointIds": [1]
  }
}
{
  "seq": 20,
  "type": "event",
  "event": "output",
  "body": {
    "category": "stdout",
    "output": "Computing factorial of 5\n",
    "source": { "name": "main.kt", "path": "/home/dev/project/src/main/kotlin/main.kt" },
    "line": 10
  }
}
{
  "seq": 8,
  "type": "event",
  "event": "thread",
  "body": { "reason": "started", "threadId": 1 }
}
EVENTexited event
{ "seq": 50, "type": "event", "event": "exited", "body": { "exitCode": 0 } }
EVENTterminated event
{ "seq": 51, "type": "event", "event": "terminated" }
{
  "seq": 7,
  "type": "event",
  "event": "process",
  "body": {
    "name": "com.example.debug.MainKt",
    "systemProcessId": 12345,
    "isLocalProcess": true,
    "startMethod": "launch"
  }
}
{
  "seq": 9,
  "type": "event",
  "event": "breakpoint",
  "body": {
    "reason": "changed",
    "breakpoint": { "id": 1, "verified": true, "line": 4 }
  }
}

Live Debug Session

Step-by-step walkthrough of debugging a Kotlin factorial function

Kotlinmain.kt — Our target program
1package com.example.debug
2
3class="kt-keyword">fun factorial(n: Int): Long {
4 class="kt-keyword">if (n <= class="kt-number">1) class="kt-keyword">return class="kt-number">1L class=class="kt-string">"kt-comment">// Line class="kt-number">4: base case
5 class="kt-keyword">return n * factorial(n - class="kt-number">1) class=class="kt-string">"kt-comment">// Line class="kt-number">5: recursive call
6}
7
8class="kt-keyword">fun main() {
9 class="kt-keyword">val number = class="kt-number">5 class=class="kt-string">"kt-comment">// Line class="kt-number">9
10 println(class="kt-string">"Computing factorial of $number") class=class="kt-string">"kt-comment">// Line class="kt-number">10
11 class="kt-keyword">val result = factorial(number) class=class="kt-string">"kt-comment">// Line class="kt-number">11: we call factorial here
12 println(class="kt-string">"factorial($number) = $result") class=class="kt-string">"kt-comment">// Line class="kt-number">12
13}

Session Timeline

Follow the complete DAP message exchange. We set a breakpoint at line 4 (insidefactorial), then step through each recursive call.

request

initialize

Client connects and negotiates capabilities with the Kotlin debug adapter.

{
  "seq": 1,
  "type": "request",
  "command": "initialize",
  "arguments": {
    "clientID": "vscode",
    "adapterID": "kotlin",
    "linesStartAt1": true,
    "columnsStartAt1": true,
    "supportsVariableType": true
  }
}
response

initialize response

Adapter reports its capabilities.

{
  "seq": 1,
  "type": "response",
  "request_seq": 1,
  "command": "initialize",
  "success": true,
  "body": {
    "supportsConfigurationDoneRequest": true,
    "supportsConditionalBreakpoints": true,
    "supportsEvaluateForHovers": true,
    "supportsSetVariable": true,
    "supportsTerminateRequest": true,
    "exceptionBreakpointFilters": [
      { "filter": "caught", "label": "Caught Exceptions" },
      { "filter": "uncaught", "label": "Uncaught Exceptions", "default": true }
    ]
  }
}
event

initialized

Adapter signals it's ready for configuration.

{ "seq": 2, "type": "event", "event": "initialized" }
request

setBreakpoints

Set a breakpoint at line 4 inside the factorial function.

{
  "seq": 2,
  "type": "request",
  "command": "setBreakpoints",
  "arguments": {
    "source": { "name": "main.kt", "path": "/home/dev/project/src/main/kotlin/main.kt" },
    "breakpoints": [{ "line": 4 }]
  }
}
response

setBreakpoints response

Breakpoint verified at line 4.

{
  "seq": 3,
  "type": "response",
  "request_seq": 2,
  "command": "setBreakpoints",
  "success": true,
  "body": { "breakpoints": [{ "id": 1, "verified": true, "line": 4 }] }
}
request

configurationDone

All configuration sent. Adapter can start the debuggee.

{ "seq": 3, "type": "request", "command": "configurationDone" }
request

launch

Launch the Kotlin program with debugging enabled.

{
  "seq": 4,
  "type": "request",
  "command": "launch",
  "arguments": {
    "mainClass": "com.example.debug.MainKt",
    "projectRoot": "/home/dev/project",
    "classPaths": ["/home/dev/project/build/classes/kotlin/main"]
  }
}
event

process event

The JVM process has started.

{
  "seq": 5,
  "type": "event",
  "event": "process",
  "body": { "name": "com.example.debug.MainKt", "systemProcessId": 28456, "startMethod": "launch" }
}
event

output event

Program prints to stdout before hitting the breakpoint.

{
  "seq": 7,
  "type": "event",
  "event": "output",
  "body": { "category": "stdout", "output": "Computing factorial of 5\n" }
}
event

stopped — breakpoint hit!

factorial(5) called, execution stops at line 4.

Breakpoint hit at line 4 — first recursive call with n=5

{
  "seq": 8,
  "type": "event",
  "event": "stopped",
  "body": { "reason": "breakpoint", "threadId": 1, "allThreadsStopped": true, "hitBreakpointIds": [1] }
}
request

stackTrace

Client requests the call stack.

RESPONSEstackTrace response
{
  "seq": 10,
  "type": "response",
  "request_seq": 9,
  "command": "stackTrace",
  "success": true,
  "body": {
    "stackFrames": [
      { "id": 1, "name": "factorial", "line": 4, "column": 5,
        "source": { "name": "main.kt", "path": "/home/dev/project/src/main/kotlin/main.kt" } },
      { "id": 2, "name": "main", "line": 11, "column": 18,
        "source": { "name": "main.kt", "path": "/home/dev/project/src/main/kotlin/main.kt" } }
    ],
    "totalFrames": 2
  }
}
request

scopes + variables

Client inspects variables in the factorial frame.

RESPONSEvariables response
{
  "body": {
    "variables": [
      { "name": "n", "value": "5", "type": "Int", "variablesReference": 0 }
    ]
  }
}

Variable state: n = 5 (first call: factorial(5))

request

continue

Resume execution. factorial(4) will hit the breakpoint again.

{ "seq": 15, "type": "request", "command": "continue", "arguments": { "threadId": 1 } }
event

stopped — breakpoint (n=4)

Second recursive call hits the breakpoint.

Recursive call stack growing:
factorial(n=4) ← stopped here
factorial(n=5)
main()
request

evaluate

Use the debug console to evaluate an expression.

{
  "seq": 22,
  "type": "request",
  "command": "evaluate",
  "arguments": { "expression": "n * 2", "frameId": 1, "context": "repl" }
}
{
  "body": { "result": "8", "type": "Int", "variablesReference": 0 }
}
request

stepIn

Step into the recursive call to follow factorial(3).

{ "seq": 25, "type": "request", "command": "stepIn", "arguments": { "threadId": 1 } }
event

stopped — step complete

Now inside factorial(3).

Deeper in the recursion:
factorial(n=3) ← stopped here
factorial(n=4)
factorial(n=5)
main()
request

continue (to completion)

Let the program run to completion.

After continuing through all recursive calls:

factorial(1) = 1
factorial(2) = 2 × 1 = 2
factorial(3) = 3 × 2 = 6
factorial(4) = 4 × 6 = 24
factorial(5) = 5 × 24 = 120
event

output

Program prints the final result.

{
  "seq": 40,
  "type": "event",
  "event": "output",
  "body": { "category": "stdout", "output": "factorial(5) = 120\n" }
}
event

exited

Program exits normally with code 0.

{ "seq": 45, "type": "event", "event": "exited", "body": { "exitCode": 0 } }
event

terminated

Debug session is complete.

{ "seq": 46, "type": "event", "event": "terminated" }
request

disconnect

Client cleanly disconnects.

{
  "seq": 50,
  "type": "request",
  "command": "disconnect",
  "arguments": { "restart": false, "terminateDebuggee": false }
}

Kotlin & JVM Debugging

How DAP works with the JVM via JDWP and JDI

Architecture for JVM

When debugging Kotlin/Java, the debug adapter communicates with the JVM through the Java Debug Wire Protocol (JDWP).

IDE (VS Code)
DAP Client
kotlin-debug-adapter
DAP ↔ JDI translation
JDI (Java Debug Interface)
Java API for debugging
JDWP
Wire protocol (TCP)
JVM (debug agent)
-agentlib:jdwp=...
Kotlin Application
The debuggee

VS Code launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "kotlin",
      "request": "launch",
      "name": "Launch Factorial",
      "mainClass": "com.example.debug.MainKt",
      "projectRoot": "${workspaceFolder}",
      "classPaths": [
        "${workspaceFolder}/build/classes/kotlin/main"
      ]
    },
    {
      "type": "kotlin",
      "request": "attach",
      "name": "Attach to JVM",
      "hostName": "localhost",
      "port": 5005,
      "timeout": 10000
    }
  ]
}
KotlinJava launch for remote debug
class=class="kt-string">"kt-comment">// Launch JVM with debug agent
class=class="kt-string">"kt-comment">// suspend=y: wait for debugger
java -agentlib:jdwp=transport=dt_socket,\
server=y,suspend=y,address=*:class="kt-number">5005 \
-jar myapp.jar