@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 +22 -0
- package/README.md +109 -0
- package/dist/archlang.gbnf +119 -0
- package/dist/llms-full.txt +599 -0
- package/dist/plan.schema.json +593 -0
- package/dist/server.js +266 -0
- package/dist/server.js.map +1 -0
- package/dist/spec.llm.md +241 -0
- package/package.json +53 -0
- package/server.json +24 -0
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]*
|