Skip to main content
Glama

Scaffold TypeScript or JavaScript Classes from a Diagram

diagrams_scaffold
Read-onlyIdempotent

Draft bounded empty exported TypeScript or JavaScript classes from entity-name candidates in a stored PlantUML or Mermaid diagram, returning source text to review and then save yourself. TypeScript is the default language.

Instructions

Draft bounded empty exported TypeScript or JavaScript classes from entity-name candidates in a stored PlantUML or Mermaid diagram, returning source text to review and then save yourself. TypeScript is the default language.

This reads heuristic entity-name candidates through the existing extractor unchanged, so candidates may include aliases, namespaces, components, participants, and call names such as charge. By default (all opt-in flags false) TypeScript mode does NOT recover class kinds, members, field types, relations, or runtime behavior. Every result says so with confidence "heuristic" plus a heuristic_warning; review the draft before saving it in the selected language yourself.

Set language="javascript" to emit empty exported JavaScript class shells only. It uses the same candidate filtering, entity cap, and diagram source byte limit as the default TypeScript shells, returns no member, type, or relation metadata, and rejects include_members/include_types/include_relations when any is true.

With include_members=true in TypeScript mode, simple top-level multiline class NAME { blocks can add member stubs. Names must be bare ASCII identifiers; quoted, aliased, generic, nested, or decorated classes are outside this subset. The first substantive line must be @startuml (PlantUML) or classDiagram (Mermaid).

Supported members: optional +, -, #, or ~ visibility; fields name: Type, Type name, or bare name; methods name(args) with an optional single-token return label, or PlantUML Type name(args). Without include_types, fields become unknown; methods accept ..._args: unknown[], return unknown, and throw the literal "Not implemented". Argument names, types, modifiers, and bodies are not recovered. Member names use the same ASCII identifier grammar and length cap as class names; constructor and prototype are excluded. Duplicate names keep their first occurrence across field/method collisions and repeated class blocks.

Unsupported syntax is skipped, including constructors, initializers, implementation bodies, and external Class : member notation. Whole-line comments, PlantUML block comments, and note bodies are ignored. Each emitted class receives at most 30 members; members_capped reports an additional recognized unique name beyond that limit. This is a heuristic subset, not a full parser.

With include_types=true in TypeScript mode (which implies include_members=true), accepted field labels resolve to a strict case-sensitive subset only: string, number, boolean, bigint, or exactly an emitted class name, optionally with one [] suffix, optionally paired with exactly one null arm in either order (for example, string | null or null | User[]); nullable labels canonicalize to Atom | null. Whitespace around arms and the separator is allowed, but not inside an atom. Bare null, undefined as a type atom, any, unknown, void, extra or duplicate arms, parentheses, other generics, objects, functions, qualified names, and nested arrays are unsupported. Classes past max_entities, skipped candidates, and nonexistent classes fall back to unknown. Accepted fields emit as name!: Type (definite assignment for strict TypeScript); every other emitted field keeps the member-mode name: unknown;. Methods stay literal throwing stubs with no recovered params or returns. Class references are heuristic labels only, with no supplied runtime value or relations claimed. typed_fields counts accepted emitted fields and unresolved_fields counts emitted fields falling back to unknown (never methods or skipped/over-cap members).

Array and ReadonlyArray accept exactly one supported primitive or emitted-class base and emit T[] or readonly T[] respectively. Literal readonly T[] is also accepted in name: Type notation for one supported base, optionally with one null arm in either order. Readonly scalars, nested arrays, and other generic forms are unsupported. Either wrapper may have one null arm. No whitespace is allowed inside wrappers and nested wrappers or array arguments or array suffixes on wrappers remain unsupported. Wrapper and nullable labels require name: Type field notation. Trimmed labels are retained up to 82 characters including whitespace around the separator. Type identifiers remain limited to 60 characters. An overlong label falls back to unknown even if removing extra whitespace would make it fit.

With include_relations=true in TypeScript mode, typed members are enabled even if include_types/include_members are false. Body field labels use the same nullable grammar as include_types, including literal readonly T[] in colon notation. Top-level Owner --> Target : role links become role!: Target;. Optional target multiplicity before Target (quoted , 0.., or 1..*) becomes role!: Target[];. Both endpoints must be selected emitted classes and independently declared plain top-level class NAME, class NAME { multiline blocks, or class NAME {} empty blocks. No endpoint classes are invented. Alias/quoted/generic/nested/implicit declarations and other arrows, multiplicities or free-text/unlabeled links are outside this subset.

Role names use the member identifier rules. Body fields/methods win collisions regardless of edge position, then the first accepted role wins. Relation fields follow body members. Comments, note bodies and nested blocks are ignored. At most 300 relationship fields are appended, within the existing class member cap. Recognized arrow lines not added (missing endpoints, invalid role, conflicts, duplicates or limits) increment relations_skipped. Unsupported grammar does not count. A novel candidate refused by either cap sets relations_capped; a class cap also sets members_capped. These are heuristic proposed fields, not recovered ownership, inheritance, runtime values or enforced cardinality.

Relation mode additionally returns include_relations:true, relations_included, relations_skipped, relations_capped, relation_limit. Type/member counts include appended fields. False or omitted include_relations preserves the three existing source and response modes.

It never writes, renders, fetches, or executes: one traversal-protected store read, pure filtering and emission, no new dependencies. Nothing lands outside the response until you save it with an explicit write. Still read-only with include_members=true or include_types=true.

Args:

  • relative_path (string, required): Path to the diagram, relative to the diagrams root

  • language ("typescript" | "javascript", default "typescript"): Output language; JavaScript is shell-only

  • max_entities (integer 1-60, default 30): Hard cap on emitted classes

  • include_members (boolean, default false): TypeScript-only heuristic member stubs plus members metadata

  • include_types (boolean, default false): TypeScript-only heuristic field types; true implies include_members=true plus typed metadata

  • include_relations (boolean, default false): TypeScript-only named-arrow fields; implies include_types and include_members

Returns: JSON with schema: { "relative_path": string, // POSIX form "language": "typescript" | "javascript", // defaults to "typescript" "source": string, // the scaffold; identical to the text block ("" when no emittable names) "entities": string[], // emitted class names in candidate order, capped "entities_included": number, // length of "entities" "entities_available": number, // valid candidates before the cap "entities_capped": boolean, // true when valid names were dropped by the cap "entity_limit": number, // the applied cap (max_entities, at most 60) "skipped_entities": string[], // invalid or reserved candidates, independent of the cap "skipped_count": number, // length of "skipped_entities" "confidence": "heuristic", "heuristic_warning": string, // selected language and mode limitations "written": false, // always false; save the source yourself // include_members=true only: // "include_members": true, // "members_included": number, // total emitted field+method stubs across emitted classes // "members_capped": boolean, // true when a 31st unique supported member was found // "member_limit": 30 // fixed per-class member cap // include_types=true only (implies the member keys above): // "include_types": true, // "typed_fields": number, // emitted fields with an accepted type label (name!: Type) // "unresolved_fields": number // emitted fields falling back to name: unknown }

Names are emitted only when they match /^[A-Za-z_][A-Za-z0-9_]*$/ at most 60 characters and avoid reserved and TypeScript-forbidden class names. Other candidates are listed in skipped_entities without further renaming. An empty candidate set returns empty source.

Examples:

  • Use when: "Draft class shells for the names in this diagram" -> relative_path="models/order.puml"

  • Use when: "Give me TypeScript stubs for the Mermaid model" -> relative_path="models/order.mmd", max_entities=10

  • Use when: "Draft JavaScript class shells from this model" -> relative_path="models/order.mmd", language="javascript"

  • Use when: "Draft shells with heuristic member stubs" -> relative_path="models/order.puml", include_members=true

  • Use when: "Draft stubs with heuristic field types" -> relative_path="models/order.puml", include_types=true

  • Use when: "Draft fields from named arrows between my declared classes" -> relative_path="models/order.mmd", include_relations=true

  • Don't use when: You want full members, types, or relations (out of scope; JavaScript emits empty shells only; TypeScript opt-in emits heuristic stubs and a strict field-type subset with unknown fallback)

  • Don't use when: You want a diagram from code (use diagrams_generate)

Error Handling:

  • Returns "Error: No diagram found at ''" if the diagram does not exist, before any extraction

  • Returns "Error: Refused to access path outside the diagrams root" if relative_path attempts to escape the diagrams directory

  • Returns "Error: ... does not have a recognized diagram extension ..." when the path has no diagram extension

  • Returns "Error: Diagram source is bytes, larger than the 1000000-byte limit ..." when the stored source exceeds the byte cap; nothing partial is returned

  • Returns a clear error when language="javascript" is combined with include_members/include_types/include_relations; this check runs before the stored diagram is read

  • An empty candidate set is not an error: source is "" with entities [] and the heuristic warning still explains the limitation

  • Unexpected internal failures return a generic "Error: Unexpected internal error ..." with isError:true and are logged to stderr without source, paths, or secrets

Input Schema

NameRequiredDescriptionDefault
languageNoOutput language (default: typescript). JavaScript emits empty exported class shells only and cannot be combined with include_members, include_types, or include_relations.typescript
max_entitiesNoHard cap on emitted class shells, an integer between 1 and 60 (default: 30). The applied cap is reported as entity_limit; anything past it is counted in entities_available and flagged by entities_capped.
include_typesNoTypeScript-only opt-in heuristic field types for member stubs (default: false). True implies include_members=true; with relations disabled, false adds no new keys. True resolves field labels to string, number, boolean, bigint, or exactly an emitted class name, optionally with one [] suffix; a field may also have exactly one null arm in either order (canonicalized as Atom | null). Array<T> and ReadonlyArray<T> also accept a supported primitive or emitted-class base and emit T[] or readonly T[]. Literal readonly T[] is accepted in colon notation only. Readonly scalars and nested arrays are unsupported. Whitespace around arms and the separator is allowed, not inside an atom. Accepted fields emit as name!: Type; all others emit as name: unknown.
relative_pathYesPath to the diagram to scaffold from, relative to the diagrams root.
include_membersNoTypeScript-only opt-in heuristic member stubs from plain class NAME { ... } blocks (default: false). With other opt-ins disabled, false keeps empty classes with no new keys; true adds throwing stubs plus members metadata.
include_relationsNoTypeScript-only opt-in named association fields (default: false). Implies include_types and include_members. Only labeled --> relations between explicitly declared, emitted plain classes are used; no inheritance or runtime values.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed5 schema fields changedv0.11.0
    • changedInput schema / properties / include_members / description
      Previous value: -"Opt-in heuristic member stubs from plain class NAME { ... } blocks only (default: false). With other opt-ins disabled, false keeps empty classes with no new keys; true adds throwing stubs plus members metadata."New value: +"TypeScript-only opt-in heuristic member stubs from plain class NAME { ... } blocks (default: false). With other opt-ins disabled, false keeps empty classes with no new keys; true adds throwing stubs plus members metadata."
    • changedInput schema / properties / include_relations / description
      Previous value: -"Opt-in named association fields (default: false). Implies include_types and include_members. Only labeled --> relations between explicitly declared, emitted plain classes are used; no inheritance or runtime values."New value: +"TypeScript-only opt-in named association fields (default: false). Implies include_types and include_members. Only labeled --> relations between explicitly declared, emitted plain classes are used; no inheritance or runtime values."
    • changedInput schema / properties / include_types / description
      Previous value: -"Opt-in heuristic field types for member stubs (default: false). True implies include_members=true; with relations disabled, false adds no new keys. True resolves field labels to string, number, boolean, bigint, or exactly an emitted class name, optionally with one [] suffix; a field may also have exactly one null arm in either order (canonicalized as Atom | null). Array<T> and ReadonlyArray<T> also accept a supported primitive or emitted-class base and emit T[] or readonly T[]. Whitespace around arms and the separator is allowed, not inside an atom. Accepted fields emit as name!: Type; all others emit as name: unknown."New value: +"TypeScript-only opt-in heuristic field types for member stubs (default: false). True implies include_members=true; with relations disabled, false adds no new keys. True resolves field labels to string, number, boolean, bigint, or exactly an emitted class name, optionally with one [] suffix; a field may also have exactly one null arm in either order (canonicalized as Atom | null). Array<T> and ReadonlyArray<T> also accept a supported primitive or emitted-class base and emit T[] or readonly T[]. Literal readonly T[] is accepted in colon notation only. Readonly scalars and nested arrays are unsupported. Whitespace around arms and the separator is allowed, not inside an atom. Accepted fields emit as name!: Type; all others emit as name: unknown."
    • addedInput schema / properties / language
      Added value: +{
      +  "default": "typescript",
      +  "description": "Output language (default: typescript). JavaScript emits empty exported class shells only and cannot be combined with include_members, include_types, or include_relations.",
      +  "enum": [
      +    "typescript",
      +    "javascript"
      +  ],
      +  "type": "string"
      +}
    • changedInput schema / properties / max_entities / description
      Previous value: -"Hard cap on emitted TypeScript classes, an integer between 1 and 60 (default: 30). The applied cap is reported as entity_limit; anything past it is counted in entities_available and flagged by entities_capped."New value: +"Hard cap on emitted class shells, an integer between 1 and 60 (default: 30). The applied cap is reported as entity_limit; anything past it is counted in entities_available and flagged by entities_capped."
  2. Addedv0.10.0

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the read-only/idempotent profile, but the description adds substantial new context: a single traversal-protected store read, the 1,000,000-byte source cap, the 30-member and 300-relation caps, first-wins collision rules, and that it never writes/renders/executes. Error handling enumerates path-escape, missing diagram, extension, and byte-limit failures.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

It is well organized mode-by-mode and front-loads the core verb, but it is far longer than needed: null-arm, Array<T>/ReadonlyArray<T>, and readonly T[] rules are re-explained at length and then echoed again in the schema. Several constraints (caps, identifier grammar) appear twice, diluting the signal an agent needs before calling.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description compensates by enumerating the return keys, the confidence/heuristic_warning marker, and mode-specific metadata. It also covers error behavior and the exact identifier grammar, so there is no obvious gap for a 6-parameter, behaviorally rich tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the input schema already documents language, max_entities, and all three opt-in flags including the implication chains and JavaScript rejection. The description restates essentially the same parameter semantics rather than adding syntax or precedence information the schema lacks, apart from noting that include_relations enables typed members independently.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence names a specific verb and resource ('Draft bounded empty exported TypeScript or JavaScript classes from entity-name candidates in a stored PlantUML or Mermaid diagram') and states the scope boundary (empty shells, heuristic, nothing saved). The 'Don't use when' block explicitly contrasts it with diagrams_generate, so an agent can separate it from the nearest sibling.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides concrete 'Use when' examples mapped to argument combinations (language="javascript", include_members=true, include_types=true, include_relations=true) and explicit 'Don't use when' exclusions pointing to diagrams_generate for the reverse direction. Mode selection is spelled out rather than inferred.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.