argdown-mcp is a sandboxed stdio MCP server that parses Argdown documents and computes Dung
grounded extensions — which arguments survive once every attack is resolved — for language-model agents. It runs
under Deno with every runtime permission denied (no filesystem, no network, no subprocess), so a
model-invoked tool cannot escalate beyond stdio. Reasoning is grounded in named primitives: Dung's abstract
argumentation framework and Caminada's three-valued labelling. A companion Claude Code plugin extends this with
Pollock's defeater frame and Walton's argument schemes.
| Surface | stdio MCP server — three tools (parse, export_json, dung_extensions) |
| Sandbox | Deno deny-by-default — file, network, subprocess, FFI, OS info, env all denied |
| Theory | Dung (1995) abstract argumentation + Caminada (2006) three-valued labelling |
| Runtime | Deno ≥2.0 (recommended) · Node ≥24 (fallback) · Linux + macOS only |
| Companion plugin | @casualtheorics/argdown-plugin — 7 Claude Code skills routing natural language to the server |
| Out of scope | Rendering (HTML / SVG / dot / PDF), non-grounded Dung semantics, statement-level attacks, Windows |
claude mcp add --scope user argdown -- deno run --no-config --no-prompt --deny-read --deny-write --deny-net --ignore-env --deny-sys --deny-run --deny-ffi npm:@casualtheorics/argdown-mcpThat one command registers the server with Claude Code under the strict Deno permission sandbox. No yarn install, no
node_modules, no local build. Deno fetches the npm bundle and runs it with every dangerous permission denied at
launch.
Other MCP clients: see Install (per MCP client) below.
MCP servers run as long-lived child processes of an LLM client. A typical Node-launched MCP server inherits its parent's ambient permissions — full filesystem read, network egress, subprocess spawn — which means a tool the model can invoke also has those permissions. This server's blessed launch denies all of them upfront:
deno run --no-config --no-prompt
--deny-read --deny-write --deny-net
--ignore-env --deny-sys --deny-run --deny-ffi
npm:@casualtheorics/argdown-mcp
| Flag | What it prevents |
|---|---|
--deny-read |
Reading any host file |
--deny-write |
Writing any host file |
--deny-net |
Outbound network requests |
--ignore-env |
Reading process environment variables |
--deny-sys |
Querying OS info (uid, hostname, network interfaces) |
--deny-run |
Spawning subprocesses |
--deny-ffi |
Loading native libraries |
--no-prompt |
Interactive permission elevation at runtime |
--no-config |
Reading deno.json / project config from CWD |
CI verifies the bundle works under this exact profile on every push to main: see
scripts/smoke-deno.sh, invoked by the Deno runtime smoke job in
.github/workflows/ci.yml. The deny set is not aspirational documentation; it is the
runtime under which the parse / JSON-export / Dung tools are smoke-tested before release.
The MCP input kind: "file" (filesystem path) is deprecated for model use because honoring it requires loosening
--deny-read. Pass Argdown as inline source instead. Operator-facing CLI use can still pass --path.
| Tool | readOnlyHint |
openWorldHint |
Side effects |
|---|---|---|---|
parse |
true |
false |
None — pure function over inline source |
export_json |
true |
false |
None |
dung_extensions |
true |
false |
None |
All three accept the same input shape:
{
"kind": "inline",
"source": "<argdown markup>"
}Parses an Argdown document and returns a structural summary plus diagnostics.
Returns:
- A summary line with statement, argument, and section counts.
- A diagnostic block listing any lexer/parser errors with
line:colpositions, and any plugin exceptions. - A
(not valid Argdown)hint when the input parses but contains only synthetic (Untitled N) statements with no relations, arguments, or sections — the Argdown parser is permissive, so this heuristic catches "definitely not Argdown" inputs.
isError: true is set when any of the following hold: lexer or parser errors are present, a plugin threw an exception,
or the document parses to only synthetic anonymous statements with no relations, arguments, or sections.
Same as parse, but also returns the full IArgdownResponse.json payload in a fenced JSON block. Statements,
arguments, relations, and sections come through as structured data.
Computes the grounded extension under Dung's abstract argumentation framework — which arguments survive once every attack has been resolved — using Caminada's three-valued labelling algorithm.
Returns:
- A summary line:
Grounded extension: N IN, M OUT, K UNDEC over A arguments and R attacks. - A fenced JSON block with the full partition:
{
"extension": {
"in": ["..."],
"out": ["..."],
"undec": ["..."]
},
"argumentCount": 0,
"attackCount": 0
}Label semantics:
- IN — accepted: every attacker of this argument is OUT.
- OUT — defeated: at least one attacker is IN.
- UNDEC — undecided: caught in an unresolved cycle.
Scope:
- Only argument-to-argument attack relations are considered (written
<X>\n - <Y>in Argdown). Statement-level attacks ([s1]\n - [s2]) are intentionally ignored — Dung's framework is abstract over arguments; lifting statement attacks belongs to a structured-argumentation layer (ASPIC+, ABA), which is out of scope. - Only the grounded semantics is computed. Preferred, stable, complete, semi-stable, and ideal semantics are out of scope for v0.2.
- Undercuts (
relationType: "undercut") target inference nodes, not arguments, and are filtered out. - A self-attacker with no external defeater is UNDEC; with an IN external defeater, it is OUT.
A four-argument reinstatement chain — the classic test of whether a Dung implementation handles defeat propagation correctly.
Input:
<A>: claim a.
- <B>
<B>: claim b.
- <C>
<C>: claim c.
- <D>: claim d.
The chain reads: D attacks C, C attacks B, B attacks A.
dung_extensions response:
Grounded extension: 2 IN, 2 OUT, 0 UNDEC over 4 arguments and 3 attacks.
Extension:
{
"extension": {
"in": ["B", "D"],
"out": ["A", "C"],
"undec": []
},
"argumentCount": 4,
"attackCount": 3
}D is unattacked → IN. C is attacked by D (IN) → OUT. B's only attacker C is now OUT → IN (
reinstated). A is attacked by B (IN) → OUT.
A three-argument odd cycle, by contrast, declines to decide:
Input:
<A>: claim a.
- <B>
<B>: claim b.
- <C>
<C>: claim c.
- <A>
dung_extensions response:
Grounded extension: 0 IN, 0 OUT, 3 UNDEC over 3 arguments and 3 attacks.
No argument can be labelled without circularly depending on another. The grounded semantics leaves all three UNDEC — the honest answer for a cycle of mutually attacking arguments.
The server's dung_extensions tool implements Dung's grounded semantics from On the acceptability of arguments and its
fundamental role in nonmonotonic reasoning, logic programming and n-person games (Phan Minh Dung, 1995). Labels are
assigned by the iterative fixpoint described in Caminada's On the issue of reinstatement in argumentation (2006): IN,
OUT, and UNDEC partition the argument set such that every IN argument has all attackers OUT, and every OUT argument has
at least one IN attacker.
The companion plugin (next section) carries additional theoretical scaffolding:
- Pollock — rebutting vs. undercutting defeaters. A rebutter attacks a conclusion directly; an undercutter attacks
the inferential link between premises and conclusion. Used by
rebut-argument. - Walton — argument schemes and critical questions. A catalogue of recurring inference patterns (expert opinion,
cause-to-effect, analogy, etc.) each with associated critical questions that probe weak points. Used by
rebut-argumentand (planned)detect-fallacies. - Toulmin — claim / data / warrant / backing. Roles assigned to statements when converting prose to Argdown. Used by
extract-argument. - Govier — the Acceptability / Relevance / Grounds triad. Three axes on which to audit a premise. Used
by
find-unsupported-premises.
Plugin skill documentation cites the primary sources directly: see
argdown-plugin/skills/references/argumentation-theory.md.
@casualtheorics/argdown-plugin ships seven skills that route natural-language requests to the MCP
tools above. Claude Code activates them automatically by trigger phrase.
| Skill | Triggers (excerpt) |
|---|---|
validate-argdown |
"validate this argdown" · "check argdown syntax" |
find-unsupported-premises |
"find weak points" · "find gaps in the support" |
trace-argument |
"trace why X follows" · "what supports Y" |
rebut-argument |
"rebut this argument" · "steelman a counter" |
extract-argument |
"extract the argument from this prose" · "convert this to argdown" |
argdown-to-prose |
"summarise this argdown" · "explain this argument map in words" |
dung-extensions |
"compute the grounded extension" · "which arguments survive" |
Install:
claude plugin install @casualtheorics/argdown-pluginThe plugin's bundled .mcp.json registers @casualtheorics/argdown-mcp through Deno with the deny-by-default sandbox
shown above. No separate setup beyond having deno ≥2.0 on PATH. See MARKETPLACE.md for the
full plugin documentation.
claude mcp add --scope user argdown -- deno run --no-config --no-prompt --deny-read --deny-write --deny-net --ignore-env --deny-sys --deny-run --deny-ffi npm:@casualtheorics/argdown-mcpEdit claude_desktop_config.json:
{
"mcpServers": {
"argdown": {
"command": "deno",
"args": [
"run",
"--no-config",
"--no-prompt",
"--deny-read",
"--deny-write",
"--deny-net",
"--ignore-env",
"--deny-sys",
"--deny-run",
"--deny-ffi",
"npm:@casualtheorics/argdown-mcp"
]
}
}
}Use the same command + args pair as the Claude Desktop block, transposed into the client's MCP server configuration
format. The strict Deno profile is identical across clients.
npx @casualtheorics/argdown-mcpNode ≥24 runs the same bundle but provides no runtime permission sandbox. Reserve this for trusted local use; the recommended posture for model-attached MCP remains the Deno launch above.
The package also ships an argdown-cli binary exposing the same three patterns directly from the shell.
# Inline source
argdown-cli parse --source "[a]: hello
+ [b]: world"
# Compute the Dung grounded extension
argdown-cli dung --path debate.argdown
# Stdin (use the bare `-` sentinel)
cat doc.argdown | argdown-cli export-json -| CLI command | MCP tool |
|---|---|
argdown-cli parse |
parse |
argdown-cli export-json |
export_json |
argdown-cli dung |
dung_extensions |
Pass exactly one of --source <text>, --path <file>, or - (stdin). Add --json to emit the full MCP envelope (
{ content, isError? }) verbatim — useful for piping into jq. The MCP path input is deprecated; the direct CLI still
supports --path because the operator chose to invoke it.
Exit codes: 0 success · 1 the shaped result is flagged as an error · 2 argument-validation failure.
flowchart LR
Source["Argdown source<br/>(kind: inline)"] --> Parser["ParserPlugin"]
Parser --> Model["ModelPlugin"]
Model --> Route{"Tool"}
Route -->|" parse "| Shape["shapeResponse"]
Route -->|" export_json "| Export["JSONExportPlugin"]
Export --> Shape
Route -->|" dung_extensions "| Dung["dungGrounded<br/>(Caminada labelling)"]
Dung --> ShapeDung["shapeDungResponse"]
Shape --> Envelope["MCP tool result"]
ShapeDung --> Envelope
The server registers three @argdown/core plugins: ParserPlugin, ModelPlugin, and JSONExportPlugin. Rendering
plugins (html, svg, dot, pdf), selection/color/tag/group plugins, and argdown.config.json discovery are
intentionally excluded. The goal is a minimal, deterministic parse surface.
The package ships as a single self-contained dist/server.js (~6 MB) with no runtime npm dependencies. @argdown/core,
@argdown/node, the MCP SDK, and Zod are bundled by tsup; a createRequire ESM shim satisfies the CJS dependencies
that @argdown/node pulls in transitively.
The wire input schema is a flat { kind, source?, path? } object rather than a discriminated union, because the MCP
SDK's normalizeObjectSchema does not accept z.discriminatedUnion(...) — it silently emits an empty schema to
clients. Handler-side validation enforces the kind / field invariant.
| Excluded | Why |
|---|---|
| Rendering (HTML, SVG, dot, PDF) | This is a headless parse and reasoning surface. Rendering is @argdown/cli's job. |
| Preferred / stable / complete / semi-stable / ideal extensions | Out of scope for v0.2. Grounded is sufficient for the unique-acceptance case that motivates LLM tool use. |
| Statement-level attack lifting | Argdown's [s1] - [s2] syntax is not auto-lifted to argument attacks. Lifting belongs to a structured-argumentation layer (ASPIC+, ABA), not to Dung. |
kind: "file" for model-invoked calls |
Requires loosening --deny-read. The CLI still accepts --path for operator use. |
| Ambient permissions | The blessed runtime denies file, network, subprocess, FFI, OS info, and env. Re-enabling any of them re-introduces the risk the project exists to eliminate. |
| Windows | package.json sets "os": ["!win32"]. Cross-platform Deno support exists, but the Node compatibility path has not been verified on Windows and the CI matrix does not cover it. |
Linux and macOS. Windows is not supported (package.json sets "os": ["!win32"] and will refuse to install).
The recommended runtime is Deno ≥2.0. Node ≥24 is supported as a fallback for trusted local use, not as the recommended MCP deployment mode.
@importthrough MCP path mode is deprecated. Strict Deno launches deny file reads, sokind: "file"cannot load importer files or imports. Expand imports outside MCP and pass the complete Argdown text as inlinesource.@importcycles are detected by@argdown/node'sIncludePluginand surfaced inresponse.exceptions. The server will not hang.(not valid Argdown)hint appears when the input parses successfully but produces only syntheticUntitled Nstatements with no relations. This is the server's heuristic for catching non-Argdown content passed tokind: "inline".
See CONTRIBUTING.md for prerequisites, workflow commands, and the release procedure.
MIT. See the LICENSE file.