@chanmeng666/archlang-mcp 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Chan Meng
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # @chanmeng666/archlang-mcp
2
+
3
+ A [Model Context Protocol](https://modelcontextprotocol.io) server for
4
+ [ArchLang](https://github.com/chanmeng666/archlang) — author, inspect, and repair
5
+ architectural floor plans written as code, from any MCP-speaking host.
6
+
7
+ It is a **thin stdio shim over the `@chanmeng666/archlang` library** (no subprocess,
8
+ no re-implementation): each tool wraps one pure, exported function. The core stays
9
+ **zero-dependency** — the MCP SDK lives only in this package.
10
+
11
+ ## When to prefer the CLI
12
+
13
+ ArchLang's primary agent interface is the agent-native **`arch` CLI**, not this
14
+ server. A CLI costs **nothing in an agent's context window until it is called**,
15
+ whereas an MCP tool schema sits in the window permanently. If your agent already has
16
+ a shell, prefer:
17
+
18
+ ```bash
19
+ npx @chanmeng666/archlang context # the whole language + workflow + CLI + errors, one call
20
+ npx @chanmeng666/archlang compile plan.arch -o out.svg --json
21
+ ```
22
+
23
+ This MCP server exists so MCP-native hosts can **discover** ArchLang through the
24
+ registry and drive it without a shell. It is the discoverability channel, not a
25
+ replacement. See [ADR 0012](https://github.com/chanmeng666/archlang/blob/main/docs/adr/0012-mcp-shim-discoverability.md)
26
+ (which amends [ADR 0009](https://github.com/chanmeng666/archlang/blob/main/docs/adr/0009-ai-first-context-and-distribution.md)'s distribution-over-protocol stance).
27
+
28
+ ## Tools
29
+
30
+ Every tool takes ArchLang `source` (a `plan "…" { … }` string) and returns
31
+ structured JSON. `compile` also accepts `plan_json` (the Plan-JSON / RPLAN shape).
32
+
33
+ | Tool | What it does |
34
+ |------|--------------|
35
+ | `compile` | Render source (or `plan_json`) to SVG, or to zero-dep ASCII (`format:"txt"`). `accessible`, `overlay:"circulation"` opt-ins. Returns output + diagnostics. |
36
+ | `describe` | Semantic facts (rooms, areas, adjacency, doors, circulation) — verify intent without an image. |
37
+ | `lint` | Advisory `W_*` soundness warnings; `profile` selects a ruleset. |
38
+ | `validate` | Parse + resolve + lint, the ship gate. `strict` fails on warnings; `graph` checks interior-door adjacency against an intended room graph. |
39
+ | `repair` | Explicit corrector: furniture out of walls/doorways/swings → new `.arch` source + change log. Never adds doors/windows. |
40
+ | `fix` | Apply the machine-applicable fixes on a plan's diagnostics (syntactic corrector; bounded fixpoint). `unsafe` widens to `maybe-incorrect`. |
41
+ | `suggest` | Advisory door/window statements (attachment form) to resolve unreachable rooms / windowless bedrooms — data, never applied. |
42
+ | `complete` | LSP completion items in scope at a source byte offset. |
43
+
44
+ ## Resources
45
+
46
+ | URI | Content |
47
+ |-----|---------|
48
+ | `archlang://spec` | The whole language in one page (`spec.llm.md`). |
49
+ | `archlang://context` | Full agent context: spec + workflow + CLI reference + error catalog (`llms-full.txt`). |
50
+ | `archlang://schema` | JSON Schema (2020-12) for the Plan-JSON compile input. |
51
+ | `archlang://grammar` | GBNF constrained-decoding grammar for guaranteed-parseable generation. |
52
+
53
+ ## Install
54
+
55
+ ### Claude Code
56
+
57
+ ```bash
58
+ claude mcp add archlang -- npx -y @chanmeng666/archlang-mcp
59
+ ```
60
+
61
+ ### Claude Desktop
62
+
63
+ Add to `claude_desktop_config.json` (Settings → Developer → Edit Config):
64
+
65
+ ```json
66
+ {
67
+ "mcpServers": {
68
+ "archlang": {
69
+ "command": "npx",
70
+ "args": ["-y", "@chanmeng666/archlang-mcp"]
71
+ }
72
+ }
73
+ }
74
+ ```
75
+
76
+ ### Cursor
77
+
78
+ Add to `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global):
79
+
80
+ ```json
81
+ {
82
+ "mcpServers": {
83
+ "archlang": {
84
+ "command": "npx",
85
+ "args": ["-y", "@chanmeng666/archlang-mcp"]
86
+ }
87
+ }
88
+ }
89
+ ```
90
+
91
+ ### VS Code
92
+
93
+ Add to `.vscode/mcp.json`:
94
+
95
+ ```json
96
+ {
97
+ "servers": {
98
+ "archlang": {
99
+ "type": "stdio",
100
+ "command": "npx",
101
+ "args": ["-y", "@chanmeng666/archlang-mcp"]
102
+ }
103
+ }
104
+ }
105
+ ```
106
+
107
+ ## License
108
+
109
+ MIT © Chan Meng
@@ -0,0 +1,119 @@
1
+ # ArchLang GBNF grammar — a constrained-decoding grammar for llama.cpp-style samplers.
2
+ #
3
+ # GENERATED by scripts/gen-gbnf.ts from src/grammar/tokens.ts (keywords/operators)
4
+ # plus USE_KINDS / FURNITURE_ANCHORS from src/ast.ts. Do NOT edit by hand — run
5
+ # `npx tsx scripts/gen-gbnf.ts`; CI checks drift (test/gbnf-drift.test.ts).
6
+ #
7
+ # Feed it to a constrained sampler (llama.cpp `--grammar-file`, or the `grammar`
8
+ # field of a server completion) to force a model to emit syntactically well-formed
9
+ # ArchLang. This is a PRACTICAL grammar: intentionally a superset / approximation of
10
+ # the hand-written parser (src/parser.ts) — it constrains the keyword-first
11
+ # statement shapes and enum vocabularies but accepts some token spacings and
12
+ # attribute orders the parser is stricter about. It must never REJECT a valid .arch;
13
+ # the parser + its catalogued diagnostics remain the source of truth for validity.
14
+ # Deterministic bytes (no version / date).
15
+
16
+ root ::= ws "plan" rws string ws "{" ws ( plan-stmt ws )* "}" ws
17
+ plan-stmt ::= setting | title-stmt | acc-stmt | theme-stmt | style-stmt | component-stmt | import-stmt | strip-stmt | block-stmt
18
+ block-stmt ::= element | let-stmt | for-stmt | if-stmt | while-stmt | set-stmt | instance-stmt | assign-stmt
19
+ element ::= wall-stmt | room-stmt | door-stmt | window-stmt | opening-stmt | furniture-stmt | dim-stmt | column-stmt
20
+ setting ::= units-stmt | grid-stmt | scale-stmt | north-stmt | dims-stmt
21
+ units-stmt ::= "units" rws "mm"
22
+ grid-stmt ::= "grid" rws number
23
+ scale-stmt ::= "scale" rws number ws ":" ws number
24
+ north-stmt ::= "north" rws north-dir
25
+ north-dir ::= "up" | "down" | "left" | "right" | number
26
+ dims-stmt ::= "dims" rws "auto" ( rws dims-mode )?
27
+ dims-mode ::= "overall" | "rooms" | "walls" | "all"
28
+ acc-stmt ::= ( "accTitle" | "accDescr" ) rws string
29
+ title-stmt ::= "title" ws "{" ws ( title-field ws )* "}"
30
+ title-field ::= ( "project" | "drawn_by" | "date" ) rws string
31
+ theme-stmt ::= "theme" ( rws "from" rws string | rws ident ( ws theme-block )? | ws theme-block )
32
+ theme-block ::= "{" ws ( theme-entry ws )* "}"
33
+ theme-entry ::= ident ws ":"? ws config-value
34
+ style-stmt ::= "style" rws ident ws "{" ws ( style-entry ws )* "}"
35
+ style-entry ::= ident ws ":"? ws config-value
36
+ config-value ::= string | number
37
+ component-stmt ::= "component" rws ident ws "(" ws param-list? ws ")" ws block
38
+ param-list ::= ident ( ws "," ws ident )*
39
+ import-stmt ::= "import" rws string ws ":" ws import-items
40
+ import-items ::= "*" | import-item ( ws "," ws import-item )*
41
+ import-item ::= ident ( rws "as" rws ident )?
42
+ let-stmt ::= "let" rws ident ( ws "(" ws param-list? ws ")" )? ws "=" ws expr
43
+ for-stmt ::= "for" rws ident rws "in" ws expr ws block
44
+ if-stmt ::= "if" ws expr ws block ( ws "else" ws block )?
45
+ while-stmt ::= "while" ws expr ws block
46
+ set-stmt ::= "set" rws element-kw ws "(" ws set-entries? ws ")"
47
+ element-kw ::= "wall" | "room" | "door" | "window" | "opening" | "furniture" | "dim" | "column"
48
+ set-entries ::= set-entry ( ws "," ws set-entry )*
49
+ set-entry ::= ident ws ":" ws expr
50
+ instance-stmt ::= ident ws "(" ws ( expr ( ws "," ws expr )* )? ws ")"
51
+ assign-stmt ::= ident ws "=" ws expr
52
+ block ::= "{" ws ( block-stmt ws )* "}"
53
+ strip-stmt ::= "strip" rws strip-dir rws "at" ws point rws "gap" ws expr ( rws strip-cross )? ws "{" ws ( strip-room ws )* "}"
54
+ strip-dir ::= "right" | "left" | "down" | "up"
55
+ strip-cross ::= ( "height" | "width" ) ws expr
56
+ strip-room ::= "room" rws id-opt "size" ws strip-size ( ws room-label )? ( ws room-uses )?
57
+ strip-size ::= expr ( ws "x" ws expr )?
58
+ wall-stmt ::= "wall" rws id-opt ident rws "thickness" ws expr ( ws wall-material )? ws "{" ws ( point ws )* ( "close" ws )? "}"
59
+ wall-material ::= "material" rws ident ( rws ( "scale" | "angle" ) ws expr ){0,2}
60
+ room-stmt ::= "room" rws id-opt room-pos ws "size" ws dims ( ws room-label )? ( ws room-uses )?
61
+ room-pos ::= "at" ws point | rel-dir rws ident ( rws "align" rws ident )? ( rws "gap" ws expr )?
62
+ rel-dir ::= "right-of" | "left-of" | "below" | "above"
63
+ room-label ::= "label" ws string
64
+ room-uses ::= "uses" rws use-kind ( rws use-kind )*
65
+ use-kind ::= "living" | "kitchen" | "dining" | "bedroom" | "bath" | "wc" | "hall" | "circulation" | "storage" | "utility" | "office" | "entry"
66
+ door-stmt ::= "door" rws id-opt opening-target ws "width" ws expr ( ws door-clause )*
67
+ door-clause ::= "wall" rws ident | "hinge" rws hinge-val | "swing" rws swing-val
68
+ hinge-val ::= "near" rws ( "start" | "end" ) | "left" | "right"
69
+ swing-val ::= "into" rws ident | "in" | "out"
70
+ window-stmt ::= "window" rws id-opt opening-target ws "width" ws expr ( ws "wall" rws ident )?
71
+ opening-stmt ::= "opening" rws id-opt opening-target ws "width" ws expr ( ws "wall" rws ident )?
72
+ opening-target ::= "at" ws point | "on" rws ident rws "at" ws attach-pos
73
+ attach-pos ::= "center" | number ws "%" | number
74
+ furniture-stmt ::= "furniture" rws id-opt ident rws furn-pos ( ws furn-clause )*
75
+ furn-pos ::= "against" rws "wall" rws ident ( ws against-opt )* | "in" rws ident rws in-place | "at" ws point
76
+ against-opt ::= "segment" ws expr | "offset" ws expr | "side" rws ident
77
+ in-place ::= "centered" | "anchor" rws anchor ( ws "inset" ws expr )?
78
+ anchor ::= "top-left" | "top" | "top-right" | "left" | "center" | "right" | "bottom-left" | "bottom" | "bottom-right"
79
+ furn-clause ::= "size" ws dims | "label" ws string | "rotate" ws expr | "in" rws ident
80
+ dim-stmt ::= "dim" ws point ws "->" ws point ( ws "offset" ws expr )? ( ws "text" ws string )?
81
+ column-stmt ::= "column" rws id-opt "at" ws point ws "size" ws dims
82
+ id-opt ::= ( "id" ws "=" ws ident rws )?
83
+ point ::= "(" ws expr ws "," ws expr ws ")"
84
+ dims ::= expr ws "x" ws expr
85
+ expr ::= or-expr
86
+ or-expr ::= and-expr ( ws "||" ws and-expr )*
87
+ and-expr ::= eq-expr ( ws "&&" ws eq-expr )*
88
+ eq-expr ::= cmp-expr ( ws eq-op ws cmp-expr )*
89
+ eq-op ::= "==" | "!="
90
+ cmp-expr ::= range-expr ( ws cmp-op ws range-expr )*
91
+ cmp-op ::= "<=" | ">=" | "<" | ">"
92
+ range-expr ::= add-expr ( ws ".." ws add-expr )*
93
+ add-expr ::= mul-expr ( ws add-op ws mul-expr )*
94
+ add-op ::= "+" | "-"
95
+ mul-expr ::= unary-expr ( ws mul-op ws unary-expr )*
96
+ mul-op ::= "*" | "/" | "%"
97
+ unary-expr ::= unary-op ws unary-expr | postfix-expr
98
+ unary-op ::= "-" | "+" | "!"
99
+ postfix-expr ::= atom ( ws "[" ws expr ws "]" )*
100
+ atom ::= number | string | array | if-expr | call | ref | "(" ws expr ws ")"
101
+ call ::= ident ws "(" ws ( expr ( ws "," ws expr )* )? ws ")"
102
+ ref ::= ident
103
+ array ::= "[" ws ( expr ( ws "," ws expr )* )? ws "]"
104
+ if-expr ::= "if" ws expr ws "{" ws expr ws "}" ws "else" ws "{" ws expr ws "}"
105
+ number ::= digits frac?
106
+ digits ::= [0-9]+
107
+ frac ::= "." digits
108
+ ident ::= [a-zA-Z_] [a-zA-Z0-9_]*
109
+ string ::= "\"" str-char* "\""
110
+ str-char ::= str-plain | str-esc | interp
111
+ str-plain ::= [^"\\{}\n]
112
+ str-esc ::= "\\" ( [^\n] | "\n" )
113
+ interp ::= "{" ws expr ws "}"
114
+ ws ::= sp cont*
115
+ rws ::= sp1 ws
116
+ sp ::= [ \t\r]{0,80}
117
+ sp1 ::= [ \t\r] | comment "\n" | "\n"
118
+ cont ::= comment "\n" sp | "\n" sp
119
+ comment ::= "#" [^\n]*