jflap-automata
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
JFLAP Automata Assistant — MCP Server
Claude is the reasoning layer (classifies the problem, designs states/transitions,
explains results). This server is the execution layer: it builds the formal
machine, validates it independently, serializes it to a JFLAP .jff file, and
simulates test strings with real nondeterministic search — Claude never
fabricates acceptance/rejection results.
This README covers the overview and setup. For the complete 33-tool
reference, resource limits, memory lifecycle, the GUI automation
investigation, and project history, see
DOCUMENTATION.md.
What's implemented
Component | Status |
MCP server + tool discovery (33 tools) | done |
PDA builder, validator, simulator (nondeterministic BFS), JFLAP export | done |
DFA/NFA builder, validator, simulator (subset construction), JFLAP export | done |
Turing Machine builder, validator, tape simulator, JFLAP export | done |
TM reject-state export correctness (no outgoing transitions, never | done |
CFG builder, validator, epsilon/unit/useless transformations, bounded leftmost-derivation | done |
CFG exact membership via Earley parsing | done |
JFLAP GUI automation: process control, menu navigation, file opening | real-GUI-verified — see |
JFLAP GUI automation: simulation input entry | confirmed impossible on this Java/macOS bridge — Python simulators remain authoritative |
AI-powered Automata Workbench: | done |
Bounded, LRU-evicting automaton/grammar storage + 6 management tools | done |
Server-enforced resource limits (every search parameter has a hard maximum) | done |
AppleScript-injection-hardened GUI automation, verified against the real interpreter | done |
373/373 pytest tests pass. Most call the underlying builder/validator/
simulator/storage functions directly (in-process, no MCP transport) —
the fastest way to cover algorithmic correctness. A separate, genuinely
real check also exists: tests/test_mcp_stdio.py spawns server.py as
an actual subprocess and drives it over the SDK's real stdio transport
(stdio_client + ClientSession) for 3 representative tools
(create_fa+simulate_automaton, create_cfg+check_cfg_membership,
plus a full list_tools() call asserting all 6 of its spot-checked tools
appear) — this is deliberately a protocol-wiring/schema check, not a
claim that every one of the 33 tools has been exercised over the wire;
the in-process tests are what cover each tool's actual behavior.
JFLAP 7.1 is installed on this machine and has been driven for real —
launched, closed gracefully, had files opened into it via the MCP server
with the result visually confirmed (title bar + canvas matching the
generated automaton exactly), and had its menus navigated through real
Accessibility calls. Automated simulation input entry was investigated
via five independent real techniques and confirmed — not assumed — to be
unsupported by this specific Java/AWT-to-Cocoa bridge. The project's
settled design: Python simulators stay the sole source of truth for
results; JFLAP is for visual display and optional manual verification.
See DOCUMENTATION.md's "GUI automation" section for
the full investigation and architecture.
storage/generated/*.jff holds every generated file; open it from JFLAP via
File > Open, or ask Claude to call open_in_jflap (best-effort open -a JFLAP,
falls back to manual instructions if JFLAP.app isn't installed).
Related MCP server: arc-diagram
Architecture
At its simplest, this is a straight pipeline:
Claude / MCP client
|
v
MCP server (server.py)
|
v
Builders (builders/*.py -- construct the Automaton/Grammar)
|
v
Validators (validators/*.py -- independent structural checks)
|
v
Simulators / CFG parser
(simulators/*.py -- fa/pda/tm_simulator.py, cfg_processor.py, earley_parser.py)
|
v
JFF generator (generators/jff_generator.py -- Automaton/Grammar -> JFLAP .jff XML)
|
v
Optional JFLAP desktop integration (gui/*.py -- launch, open, screenshot, close)Python is the authoritative simulation engine; JFLAP is an optional visual companion. Every ACCEPTED/REJECTED/MEMBER result a caller receives comes from the Simulators/CFG-parser step above — never from JFLAP. The last step (JFLAP desktop integration) is entirely optional: nothing above it depends on JFLAP being installed, and nothing in it can override or substitute for a Python-computed result. The fuller diagram below shows the same pipeline with the branching construction paths (DFA/NFA/PDA/TM/CFG) and the two in-memory stores (automata, grammars) made explicit:
flowchart TD
User["User"] --> Claude["Claude\n(classifies the problem, designs states/transitions, explains results)"]
Claude --> MCPClient["MCP Client\n(Claude Desktop / Claude Code)"]
MCPClient -->|stdio, MCP protocol| Server["MCP Server -- server.py\n(33 tools)"]
Server --> Orchestration["Construction & orchestration\n(create_*, solve_automata_problem)"]
Orchestration --> DFA["DFA / NFA\nbuilders/fa_builder.py"]
Orchestration --> PDA["PDA\nbuilders/pda_builder.py"]
Orchestration --> TM["Turing Machine\nbuilders/tm_builder.py"]
Orchestration --> CFG["CFG\nbuilders/cfg_builder.py"]
DFA --> Validators["Validators\nvalidators/*.py\n(independent structural checks)"]
PDA --> Validators
TM --> Validators
CFG --> Validators
Validators --> Simulators["Python Simulators -- AUTHORITATIVE EXECUTION\nsimulators/*.py\n(fa/pda/tm_simulator.py, cfg_processor.py, earley_parser.py)"]
Simulators --> JFF["JFLAP .jff export\ngenerators/jff_generator.py"]
JFF --> GUI["JFLAP 7.1 GUI integration -- gui/*.py\n(launch, open, screenshot, close -- SUPPORTED, non-authoritative)"]
Simulators -.->|"ACCEPTED / REJECTED / MEMBER\n(always Python-verified)"| Server
GUI -.->|"visual confirmation only\n(never a simulation result)"| Server
Server --> MCPClient
style Simulators fill:#2d6a4f,color:#fff
style GUI fill:#555,color:#fffThe authority boundary, explicitly: every ACCEPTED/REJECTED/MEMBER
result Claude reports comes from the Python simulators (green above),
never from JFLAP. JFLAP's GUI integration (gray above) is a separate,
genuinely useful but non-authoritative layer — launching the app,
opening a generated .jff file so a human can see the automaton, and
taking a screenshot. run_jflap_simulation deliberately reports
NOT_SUPPORTED rather than a guessed result, because typing a test
string into JFLAP's own dialogs cannot be automated on this platform
(see DOCUMENTATION.md's "Known limitations" and
"GUI automation" sections) — the Python simulators are never bypassed,
with or without JFLAP installed.
Screenshots
All real captures from the actual running application — none are
fabricated or AI-generated. See docs/screenshots/README.md
for how each one was captured and verified. Captured before the storage-
lifecycle tools were added, so the /mcp panel screenshot below shows 27
tools, not today's 33 — the panel itself is still genuine, just from an
earlier point in the project.
|
|
|
|
|
|
|
End-to-End Demonstration
examples/end_to_end_demo.py is a
standalone script that exercises the project's actual DFA/PDA/TM/CFG
builders, validators, simulators, and JFLAP exporters directly — no
Claude, no MCP protocol, no network, no API keys. It's the fastest way
to confirm the whole non-GUI pipeline works on your machine.
python examples/end_to_end_demo.pyWhat it does
Six stages, each printing its own status: construct a DFA ("ends in
01") and simulate 4 test strings; construct the project's canonical
a^(2n)b^n PDA and simulate an accepted/rejected pair; construct a
small "even number of 1s" Turing Machine and report its final
configuration; construct a small CFG and check membership of one member
and one non-member string via the exact Earley parser
(check_cfg_membership uses); re-run the TM with
trace_format="compact" and reconstruct its initial and final
configurations; and finally export all four as .jff files.
Example output
==========================================================
JFLAP MCP -- AUTOMATA WORKBENCH DEMO
==========================================================
[1/6] DFA CONSTRUCTION
Status: SUCCESS
Validation: VALID
Input '1101': ACCEPTED
Input '1010': REJECTED
Input '1111': REJECTED
Input '01': ACCEPTED
[2/6] PDA CONSTRUCTION
Status: SUCCESS
Validation: VALID
Input 'aab': ACCEPTED
Input 'aabb': REJECTED
...
[6/6] JFLAP EXPORT
DFA -> .../storage/generated/demo/demo_dfa.jff
...(Full output is reproducible by running the command yourself — see
docs/screenshots/06-automata-workflow.png and
07-tm-compact-trace.png for real captures of this exact script's
output.)
Optional arguments
Flag | Effect |
| Skip JFLAP entirely, including the export stage's JFLAP-open attempt. |
| Open the generated DFA in a real JFLAP window after export (requires Java + a configured |
| Where generated |
Run python examples/end_to_end_demo.py --help for the same, from the
script itself.
Requirements
Only this project's existing virtual environment
(pip install -r requirements.txt) — no JFLAP, no Claude, and no
network access are required for the default run. --open-jflap is the
only thing that needs JFLAP/Java set up.
What this demo proves and doesn't
Proves the DFA/PDA/TM/CFG builders, validators, and simulators work
correctly end-to-end, independent of Claude or MCP, that .jff export
works for all four automaton/grammar types, and that the TM compact-
trace architecture and reconstruct_at_step work as documented. Does
not exercise the MCP protocol itself (see tests/test_mcp_stdio.py
for that) or demonstrate JFLAP GUI simulation input, which remains
unsupported on this platform regardless of what this script does.
Project layout
jflap-mcp/
├── server.py # MCP tool definitions, wires everything together
├── models.py # shared State/Transition/Automaton + Grammar/Production dataclasses
├── resource_limits.py # per-operation default + hard-maximum resource limits, enforced server-side
├── storage.py # BoundedStore: thread-safe, LRU-evicting, pin-aware automaton/grammar storage
├── reporting.py # transition-table/production-list/summary formatters (pure, no new logic)
├── builders/
│ ├── common.py # shared State-construction helper (build_states) used by all three builders below
│ ├── pda_builder.py
│ ├── fa_builder.py # DFA + NFA
│ ├── tm_builder.py
│ └── cfg_builder.py
├── validators/
│ ├── common.py # shared single-character-symbol check used by both validators below
│ ├── automata_validator.py # structural + semantic checks for DFA/NFA/PDA/TM, optional test_cases cross-check
│ └── cfg_validator.py # structural checks for Grammar objects
├── generators/jff_generator.py # Automaton -> JFLAP .jff XML; Grammar -> JFLAP grammar .jff XML
├── simulators/
│ ├── pda_simulator.py # nondeterministic BFS over (state, input, stack) configs
│ ├── fa_simulator.py # epsilon-closure subset simulation
│ ├── tm_simulator.py # tape/head simulation, halt-on-no-transition semantics
│ ├── cfg_processor.py # nullable-set/epsilon-removal, unit-production elimination,
│ │ # useless-symbol removal, bounded leftmost-derivation search
│ └── earley_parser.py # exact CFG membership: Predictor/Scanner/Completer chart parser
├── jflap/simple_launcher.py # minimal file-based open: best-effort `open -a JFLAP <file>` (renamed
│ # from jflap_launcher.py to avoid a naming collision with gui/jflap_launcher.py)
├── gui/ # real JFLAP desktop automation (separate from simulators/)
│ ├── jflap_launcher.py # locate jar (env var/config/search, never hardcoded), launch via java -jar
│ ├── jflap_automation.py # AppleScript/Accessibility menu + keystroke automation; _applescript_quote
│ │ # renders any non-printable-ASCII character as `character id <n>`, never
│ │ # raw-interpolated, so a crafted value can't inject additional AppleScript
│ ├── screenshot_manager.py # `screencapture`-based, unique non-overwriting filenames
│ ├── jflap_controller.py # orchestrates the three above; the only module server.py imports from gui/;
│ │ # also holds GUI_TOOL_CAPABILITIES, the structured FULL/PARTIAL matrix
│ ├── verification.py # Python-vs-JFLAP comparison: MATCH/MISMATCH/INCONCLUSIVE/JFLAP_UNAVAILABLE
│ └── jflap_config.json # optional: {"jar_path": "/full/path/to/JFLAP.jar"} (not checked in; user-provided)
├── storage/generated/ # output .jff files land here (gitignored, recursive)
├── outputs/screenshots/ # GUI automation screenshots land here (gitignored, recursive; unique filenames, never overwritten)
├── examples/end_to_end_demo.py # standalone demo: no Claude, no MCP, no network -- see "End-to-End Demonstration"
├── docs/screenshots/ # real application screenshots for README.md -- see docs/screenshots/README.md
├── pyproject.toml / requirements.txt / uv.lock # see "Setup" -- pip and uv are both supported, kept in sync
├── DOCUMENTATION.md # full tool reference, architecture deep-dive, GUI investigation, project history
└── tests/ # pytest suite (373 tests, all passing) + bench_tm_trace.pySetup
Two supported install paths — pick one. Both install the exact same
dependency versions (uv.lock and requirements.txt are kept in sync;
see "Dependencies" below for the verified version range).
Option A: pip
git clone <this-repo-url> jflap-mcp
cd jflap-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python3 -m pytest tests/ -q # sanity checkOption B: uv (faster, reproducible via the committed lockfile)
git clone <this-repo-url> jflap-mcp
cd jflap-mcp
uv sync --extra dev # creates .venv, installs runtime + pytest from the lockfile
uv run pytest tests/ -q # sanity check--extra dev is required for the sanity-check command specifically —
pytest is declared as an optional (dev) dependency, so a plain
uv sync (confirmed by testing both) installs only the runtime
dependency (mcp[cli]) and intentionally skips it, matching what an end
user running the server (not its tests) actually needs. (Install uv
itself first if you don't have it: pip install uv, or see
https://docs.astral.sh/uv/getting-started/installation/.) Regenerate the
lockfile with uv lock after any dependency change in pyproject.toml.
Requires Python 3.10+ (the codebase uses X | None union-type
annotations, e.g. in simulators/tm_simulator.py, which need 3.10 or
later). Tested on Python 3.10 and 3.14 by actually running the full test
suite on both — not assumed from the requires-python declaration alone
(see DOCUMENTATION.md's "Known limitations" for the
exact versions tested). No OS-specific code runs in this path — the
Python simulators, builders, validators, and MCP server all work
identically on macOS, Linux, and Windows. GUI automation (the next
section) is macOS-only and has not been tested on other platforms —
see "Platform support" below.
Optional: enabling real JFLAP GUI automation
The Python simulators and MCP tools all work with zero setup. GUI automation (opening files in JFLAP, screenshots, visual comparison) additionally needs, all currently satisfied on this machine:
Java —
openjdk 26.0.2via Homebrew. Check withjava -version.JFLAP.jar — download from https://www.jflap.org, then either:
export JFLAP_JAR_PATH=/full/path/to/JFLAP.jar, orwrite
{"jar_path": "/full/path/to/JFLAP.jar"}togui/jflap_config.json. Nothing here guesses a path that wasn't actually verified to exist —locate_jflap_jar()checks a short list of conventional locations (~/JFLAP/,~/Downloads/,/Applications/, …) but only ever returns one that's confirmed on disk, never an assumption.
macOS Automation permission for whatever process runs
server.py— System Settings > Privacy & Security > Automation, allow it to control "System Events".macOS Accessibility permission for the same process — System Settings > Privacy & Security > Accessibility. Separate from Automation; required for menu clicks and window inspection.
macOS Screen Recording permission for the same process — System Settings > Privacy & Security > Screen Recording. Required for
capture_jflap_screenshot.
Call get_jflap_environment_status (an MCP tool, or
gui.jflap_controller.get_environment_status() directly) at any time to
see exactly which of these are satisfied right now — it reports Java,
the jar path, the running JFLAP process (if any), and both Automation and
Accessibility permission status independently (one can be granted without
the other).
Not covered by any of the above: automated entry of a test string
into JFLAP's own simulation dialogs. This was investigated thoroughly
(five independent techniques against a real running JFLAP 7.1) and
confirmed to be a structural limitation of this Java/macOS Accessibility
bridge, not a missing permission — see DOCUMENTATION.md's
"GUI automation" section for the full investigation and why the Python
simulators remain the authoritative source of simulation results
regardless.
Dependencies
mcp[cli]>=2.0,<3.0 is the only runtime dependency, and this range is
verified, not assumed: mcp 1.x exposed a completely different API
(FastMCP from mcp.server.fastmcp); 2.x renamed it to MCPServer
(from mcp.server.mcpserver import MCPServer) and moved ToolError to
mcp.server.mcpserver.exceptions — the exact imports server.py uses.
Installing mcp 1.x and importing server.py fails immediately with an
error message from the SDK itself confirming the rename. The lower bound
(>=2.0) was confirmed by installing mcp==2.0.0 (the first stable 2.x
release) in an isolated environment and running this project's entire
pytest suite — including the real, non-mocked MCP stdio-protocol tests in
tests/test_mcp_stdio.py — against it; everything passed, and the same
suite was separately run against mcp==2.3.0 (what this project's own
.venv actually uses). Neither claim extends to every 2.x point release
in between — only these two were actually tested. <3.0 guards against
a future rename of the same kind; no mcp 3.x exists yet.
Claude Desktop configuration
Edit ~/Library/Application Support/Claude/claude_desktop_config.json and
add (merge, don't replace, if the file already has other servers).
Replace /absolute/path/to/jflap-mcp with where you actually cloned this
repo — both command and the arg must be absolute paths, not ~ or a
relative path:
{
"mcpServers": {
"jflap-automata": {
"command": "/absolute/path/to/jflap-mcp/.venv/bin/python3",
"args": [
"/absolute/path/to/jflap-mcp/server.py"
]
}
}
}Claude Code / VS Code configuration
Claude Code reads the same shape from a project-local .mcp.json at the
repo root (this file is gitignored — it's machine-specific, so each
developer creates their own):
{
"mcpServers": {
"jflap-automata": {
"type": "stdio",
"command": "/absolute/path/to/jflap-mcp/.venv/bin/python3",
"args": ["/absolute/path/to/jflap-mcp/server.py"],
"env": {
"JFLAP_JAR_PATH": "/absolute/path/to/JFLAP.jar",
"PYTHONUNBUFFERED": "1"
}
}
}
}JFLAP_JAR_PATH and PYTHONUNBUFFERED are optional — omit the env
block entirely if you haven't set up JFLAP GUI automation yet (see
above); the Python simulators and every non-GUI tool work without it.
After editing either file, fully restart Claude Desktop / reload the
Claude Code window, then verify the connection: open Settings >
Developer/Extensions (Desktop) or run /mcp (Claude Code) and confirm
jflap-automata shows as connected with 33 tools listed.
Verifying the server on its own (without Claude)
Before wiring it into Claude at all, confirm the server itself starts cleanly and the tool list is what you expect:
# Syntax/import check -- should produce no output on success
.venv/bin/python -c "import server"
# Full MCP tool listing over the real stdio protocol
.venv/bin/python -c "
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
params = StdioServerParameters(command='.venv/bin/python3', args=['server.py'])
async with stdio_client(params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
print(len(tools.tools), 'tools:', sorted(t.name for t in tools.tools))
asyncio.run(main())
"The 33 tools you should see listed (full reference in
DOCUMENTATION.md):
Generic (work across DFA/NFA/PDA/TM):
classify_automata,validate_automaton,generate_jff,simulate_automaton,get_execution_trace,open_in_jflapConstruction:
create_pda,create_fa,create_tm,create_cfgTM-specific:
validate_tm,simulate_tm,generate_tm_jffCFG-specific:
validate_cfg,eliminate_unit_productions,derive_cfg,check_cfg_membership,generate_cfg_jffGUI automation:
get_jflap_environment_status,launch_jflap,close_jflap,open_jff_file,run_jflap_simulation,capture_jflap_screenshot,compare_python_jflapWorkbench orchestration:
solve_automata_problem,get_transition_tableStorage lifecycle management:
list_automata,list_grammars,delete_automaton,delete_grammar,clear_automata,clear_grammars
validate_automaton, generate_jff, simulate_automaton, and
get_execution_trace all dispatch on the stored automaton's type, so they
work for TM automatons too — the TM-specific tools exist alongside them
for discoverability. There is no generic equivalent for CFG tools since a
Grammar isn't a states-and-transitions automaton; it has its own tools
and its own in-memory store (_GRAMMARS, separate from _AUTOMATA).
check_cfg_membership and get_jflap_environment_status weren't in the
originally suggested tool lists — the first because without it Claude had
no way to get a conclusive CFG membership answer through MCP at all (only
a bounded search), the second because every other GUI tool needs that
environment information to answer honestly rather than guessing.
solve_automata_problem/get_transition_table are pure
orchestration/formatting over all of the above — see
DOCUMENTATION.md's "AI-Powered Automata Workbench"
section. The six storage-lifecycle tools exist because _AUTOMATA/
_GRAMMARS are now bounded and LRU-evicting (see
DOCUMENTATION.md's "Memory lifecycle" section) —
they let a caller see what's stored and free space deliberately instead
of only waiting for automatic eviction.
Platform support
Component | macOS | Linux | Windows |
Builders, validators, Python simulators, MCP server | Tested, works | Not tested — no OS-specific code in this path, likely works | Not tested — no OS-specific code in this path, likely works |
JFLAP GUI automation ( | Tested, works | Not supported — | Not supported — same reason |
File-based JFLAP open ( | Tested, works ( | Not supported — | Not supported — same reason |
If you're on Linux or Windows: every construction/validation/simulation/
.jff-export tool works identically (they're pure Python with no
platform-specific calls); only the gui/ automation layer and
open_in_jflap are macOS-only today. You can still open a generated
.jff file in JFLAP manually on any platform JFLAP itself supports.
Full documentation
DOCUMENTATION.md has everything not covered above:
the complete 33-tool reference (every parameter, return shape, and error
status), the resource-limits table (every default and server-enforced
hard maximum), the memory-lifecycle design (bounded storage, LRU
eviction, pinning), the recommended Claude custom-instructions prompt,
the AI-Powered Automata Workbench walkthrough, the PDA/TM/CFG
architecture deep-dives, the full GUI-automation investigation (five
techniques tried, why none work, the capability matrix, mocked-vs-real
tests), the project's 0.1.0 audit history, and the contributing guide.
Contributing
See DOCUMENTATION.md's "Contributing" section for
the full house rules. Short version: run the full test suite first
(.venv/bin/python -m pytest tests/ -q, or uv run pytest tests/ -q)
and confirm it's green before making changes, add tests for anything you
change, and open a PR describing what changed and why.
License
MIT — Copyright (c) 2026 Kavin.
Changelog
See CHANGELOG.md for the version history.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
SysML v2 systems engineering: generate, verify, simulate and export system models from text.
Create, validate, edit, export (markdown/svg/png/mermaid), and search JSON Canvas files.
Floor plans as code: compile, lint and repair ArchLang (.arch) floor plans to SVG, DXF and PDF.
Build, check and fix n8n workflows from Claude, Cursor or ChatGPT. Built on 1,100+ real n8n node definitions and curated templates, with a validator that repairs common mistakes (wrong node types, missing settings) and returns JSON ready to import into n8n. Sign in with a free gydmation.com account; nothing to install.
Related MCP Servers
- AlicenseBqualityBmaintenanceEnables natural language conversion of production and queueing systems into Petri net models, with simulation and PNML export via MCP tools.66MIT
- AlicenseAqualityCmaintenanceEnables MCP agents to validate architecture descriptions and scaffold complete, interactive React Flow diagram applications with multiple visual dialects, themes, exports, and verification probes.4Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables users to create, build, and repack Айрен .itx test archives from JSON specifications, then structurally and statically validate their XML, questions, answers, tags, profiles, formulas, and scenarios. It also renders standalone HTML previews of all questions, images, and declared correct answers, with CLI access for local automation.MIT
- AlicenseBqualityBmaintenanceEnables users to create, validate, view, and export system stock-and-flow maps from a versioned JSON model through MCP-capable hosts over local stdio or hosted Streamable HTTP. Supports causal-loop and nested-level mapping while keeping submitted model content with the caller and requiring no LLM provider key.529 npmGPL 3.0
A generated DFA ("ends in 01") open in JFLAP 7.1
The canonical
The "even number of 1s" TM in JFLAP
A generated CFG in JFLAP's grammar table view
Real terminal output: DFA/PDA/TM construction, validation, and simulation results
Real terminal output: the compact TM trace mode and step reconstruction