← MCP Security Catalog

Loomle 760fe09b2b3101c0ae1cfbf174a0a8f7f8a6aad8

source_git · loomle/loomle · latest release

Verified with no proven findings
Security result
8
Observed tools
Version rating
100
Change risk

Independent inventory

Observed 2026-08-25T09:11:42.554Z using mcpSecurity-inventory. Protocol 2025-06-18.

ToolCategoryRisk
agent_skillLoad a Loomle Agent Skill when the task matches its description. Call with no arguments to list resident Skills or with one exact name to load its complete instructions and Markdown references. Resident Skills: - debug-unreal-pie-with-python: Control and debug one in-process Unreal Engine Play In Editor or Simulate session through Loomle Python after applying the use-unreal-python safety workflow. Use when reproducing gameplay, querying runtime Actors or components, invoking gameplay APIs, checking state across frames, or managing PIE start and stop. - format-unreal-blueprints: Format and audit Unreal Engine Blueprint K2 graphs through Loomle using exact live node and pin geometry. Use when arranging selected Blueprint nodes, repairing spaghetti, choosing compact Helixing versus left-side data layout, straightening execution flow, separating branches, or evaluating whether a graph has near-human visual quality. - use-unreal-python: Use Loomle's unrestricted Unreal Python fallback safely when no structured Loomle interface covers the required Unreal Editor capability. Apply live context checks, capability selection, API introspection, idempotent mutations, exact continuation polling, partial-state recovery, explicit persistence, and independent result verification.
Input schema
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "enum": [
        "debug-unreal-pie-with-python",
        "format-unreal-blueprints",
        "use-unreal-python"
      ],
      "description": "Optional exact resident Agent Skill name."
    }
  },
  "additionalProperties": false
}
Annotations
{
  "readOnlyHint": true,
  "destructiveHint": false,
  "idempotentHint": true,
  "openWorldHint": false
}
editorObserve or control the Unreal Blueprint Editor. Call with no arguments for current context, or use open/close with one bare canonical SAL Blueprint or Graph Target expression.
Input schema
{
  "type": "object",
  "properties": {
    "operation": {
      "type": "string",
      "enum": [
        "context",
        "open",
        "close"
      ],
      "description": "Defaults to context when omitted. A target without an explicit open or close operation is invalid."
    },
    "target": {
      "type": "string",
      "minLength": 1,
      "description": "One bare canonical SAL Blueprint or Graph Target expression. Required for open and close; invalid for context."
    }
  },
  "additionalProperties": false
}
Annotations
{
  "readOnlyHint": false,
  "destructiveHint": false,
  "idempotentHint": true,
  "openWorldHint": false
}
projectInspect Loomle projects or bind this MCP session to one project. Call with no arguments to see the binding and candidates; pass projectId or projectRoot to bind. Binding is sticky, survives Editor restarts, and never falls through to another project while offline.
Input schema
{
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "minLength": 1,
      "description": "Stable project ID returned by project."
    },
    "projectRoot": {
      "type": "string",
      "minLength": 1,
      "description": "Directory containing exactly one .uproject file."
    }
  },
  "maxProperties": 1,
  "additionalProperties": false
}
Annotations
{
  "readOnlyHint": false,
  "destructiveHint": false,
  "idempotentHint": true,
  "openWorldHint": false
}
pythonRun Python in the bound Editor when no Loomle interface covers the capability. Before run, load use-unreal-python; for PIE also load debug-unreal-pie-with-python. Poll exactly; never replay. sal.object(value) marks UObjects for canonical projection. No dry run, rollback, cancellation, idempotency.
Input schema
{
  "type": "object",
  "oneOf": [
    {
      "properties": {
        "operation": {
          "const": "run"
        },
        "script": {
          "type": "string",
          "minLength": 1,
          "maxLength": 262144,
          "description": "Inline Unreal Editor Python defining one synchronous run() entry point."
        }
      },
      "required": [
        "operation",
        "script"
      ],
      "additionalProperties": false
    },
    {
      "properties": {
        "operation": {
          "const": "poll"
        },
        "executionId": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque handle returned by an earlier running result."
        }
      },
      "required": [
        "operation",
        "executionId"
      ],
      "additionalProperties": false
    }
  ]
}
Output schema
{
  "type": "object",
  "required": [
    "status",
    "stateMayHaveChanged"
  ],
  "properties": {
    "status": {
      "type": "string",
      "enum": [
        "running",
        "succeeded",
        "failed",
        "lost"
      ]
    },
    "executionId": {
      "type": "string",
      "minLength": 1
    },
    "stateMayHaveChanged": {
      "type": "boolean"
    },
    "result": {
      "type": "object",
      "additionalProperties": true
    },
    "error": {
      "type": "object",
      "required": [
        "code",
        "phase",
        "message",
        "retryable"
      ],
      "properties": {
        "code": {
          "type": "string",
          "minLength": 1
        },
        "phase": {
          "type": "string",
          "enum": [
            "validation",
            "staging",
            "execution",
            "result",
            "runtime"
          ]
        },
        "message": {
          "type": "string",
          "minLength": 1
        },
        "traceback": {
          "type": "string"
        },
        "retryable": {
          "type": "boolean"
        }
      },
      "additionalProperties": false
    },
    "logs": {
      "type": "array",
      "maxItems": 1000,
      "items": {
        "type": "object",
        "required": [
          "type",
          "output"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "info",
              "warning",
              "error"
            ]
          },
          "output": {
            "type": "string"
          }
        },
        "additionalProperties": false
      }
    },
    "logsTruncated": {
      "type": "boolean"
    },
    "durationMs": {
      "type": "integer",
      "minimum": 0
    },
    "elapsedMs": {
      "type": "integer",
      "minimum": 0
    },
    "projection": {
      "type": "object",
      "required": [
        "complete",
        "marked",
        "projected"
      ],
      "properties": {
        "complete": {
          "type": "boolean",
          "description": "False when a projector or Bridge integrity fault occurred."
        },
        "marked": {
          "type": "integer",
          "minimum": 0,
          "description": "Number of sal.object() markers in the script result."
        },
        "projected": {
          "type": "integer",
          "minimum": 0,
          "description": "Number of markers replaced with canonical projected views."
        }
      },
      "additionalProperties": false,
      "description": "Read-only Python sal.object() projection annex; present only when the script marked UObjects."
    },
    "continuation": {
      "type": "object",
      "required": [
        "tool",
        "arguments",
        "pollAfterMs"
      ],
      "properties": {
        "tool": {
          "const": "python"
        },
        "arguments": {
          "type": "object",
          "required": [
            "operation",
            "executionId"
          ],
          "properties": {
            "operation": {
              "const": "poll"
            },
            "executionId": {
              "type": "string",
              "minLength": 1
            }
          },
          "additionalProperties": false
        },
        "pollAfterMs": {
          "type": "integer",
          "minimum": 0
        }
      },
      "additionalProperties": false
    }
  },
  "additionalProperties": false
}
Annotations
{
  "readOnlyHint": false,
  "destructiveHint": true,
  "idempotentHint": false,
  "openWorldHint": true
}
sal_patchValidate or modify Unreal Engine objects with one ordered SAL Patch Text. The first result block is canonical SAL Result Text; metadata and diagnostics use later blocks. Use 'dry run' before risky edits.
Input schema
{
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "minLength": 1,
      "description": "Self-contained SAL Patch Text."
    }
  },
  "required": [
    "text"
  ],
  "additionalProperties": false
}
Annotations
{
  "readOnlyHint": false,
  "destructiveHint": true,
  "idempotentHint": false,
  "openWorldHint": false
}
sal_queryRead Unreal Engine objects with one self-contained SAL Query Text. The first text block is canonical SAL Result Text; diagnostics use later text blocks.
Input schema
{
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "minLength": 1,
      "description": "Self-contained SAL Query Text."
    }
  },
  "required": [
    "text"
  ],
  "additionalProperties": false
}
Annotations
{
  "readOnlyHint": true,
  "destructiveHint": false,
  "idempotentHint": true,
  "openWorldHint": false
}
sal_schema# SAL SAL is Loomle's line-oriented language for reading and changing Unreal Engine objects. It keeps UE names, native paths, enum values, property types, and editor behavior intact. ## MCP Calls - `status({})` reports Client, session, and Bridge health. - `project({})` reports project binding and candidates. - `project({ projectId: "<id>" })` binds the session to one project. - `sal_query({ text })` executes one Query Text. - `sal_patch({ text })` executes one ordered Patch Text. - `sal_schema({})` lists the nine active Domain interface cards. - `sal_schema({ module: "graph" })` returns one static card. - `agent_skill({})` lists resident Loomle workflow Skills. - `agent_skill({ name: "format-unreal-blueprints" })` loads one Skill before performing a matching specialized workflow. - `editor({})` returns the user's current UE context as SAL Result Text. - `editor({ operation: "open" | "close", target })` idempotently controls one exact Blueprint Editor or Graph document. `target` is one bare canonical SAL Target expression encoded as the JSON string value. - `python({ operation: "run", script })` is the unrestricted Unreal Python fallback when no structured Loomle interface covers the task. The script defines synchronous `run()` and returns a JSON-compatible dictionary. - If Python returns `status: "running"`, call `python` with the exact `poll` continuation it supplies. Never replay the original script. The tool-call wrapper is not SAL syntax. ## Project Binding One Loomle MCP session operates on one UE project. Start with `status({})`. If the session is unbound, call `project({})`, copy one returned `projectId`, and bind it with `project({ projectId: "<id>" })`. The binding is sticky: Loomle does not fall through to another online project merely because the selected project is offline or its Editor restarts. `sal_schema` remains available without an online project because the resident guide and Domain cards are local. ## Four Separate Concepts ```text object data = fields in {...} semantic tag = optional, erasable presentation metadata Domain Target = target { domain: ..., ... } stable reference = native identity path inside one exact Target ``` An ordinary object is always a brace expression: ```sal { id: "node-guid", type: "/Script/BlueprintGraph.K2Node_Event" } node { id: "node-guid", type: "/Script/BlueprintGraph.K2Node_Event" } ``` `node` is an optional semantic tag. Removing it cannot change identity, type, Domain, validation, creation, or execution. Object fields and context carry the information that matters. JSON literals, the retired generic label `object`, `target`, `domain`, the nine protocol-v6 Domain keywords (`asset`, `blueprint`, `class`, `graph`, `state_tree`, `widget`, `level`, `pcg`, and `pcg_component`), and `tree`, `context`, and `palette` are reserved and cannot be semantic tags or aliases. Parentheses are reserved for true calls or explicitly defined non-object syntax, such as an invoked operation: ```sal invoke @node-guid Rename(displayName: "New Name") ``` ## Domain Targets Every Query or Patch declares exactly one active Target: ```sal g = target { domain: graph, asset: "/Game/BP_Door.BP_Door", blueprintId: "11111111-1111-1111-1111-111111111111", id: "22222222-2222-2222-2222-222222222222" } query g summary ``` `target`, `domain`, and the Domain value are structural SAL keywords, not semantic tags. A Target is flat: every non-`domain` field is a non-empty JSON string, and a Target cannot contain another Target or alias. The resident catalog has nine active Domain adapters and interface cards. Query and Patch Target sets are deliberately separate: | Domain | Query Target | Canonical Result | Patch Target | | --- | --- | --- | --- | | `asset` | `domain` only, or `path` with optional `type` | `path + type` | `path + type` | | `blueprint` | `asset`, optional `id` | `asset + id` | `asset + id` | | `class` | `path` | `path` | `path` | | `graph` | `asset + id` or `asset + name`; optional `blueprintId` | `asset + blueprintId + id` | `asset + blueprintId + id` | | `state_tree` | `asset`, optional `type` | `asset + type` | `asset + type` | | `widget` | `asset`, optional `id` | `asset + id` | `asset + id` | | `level` | `asset`, optional `type` | `asset + type` | `asset + type` | | `pcg` | `asset`, optional `type` | `asset + type` | `asset + type` | | `pcg_component` | `asset + actorId + source + id + type` | same exact fields | same exact fields | The `level`, `pcg`, and `pcg_component` cards are public Query plus authored Patch interfaces; `pcg_component` edits are gated by its fail-closed async edit guard. A Domain's presence in the catalog does not grant Patch admission: every Domain's Patch surface is exactly what its static card defines. These canonical Query and Result identities are stable across Patch expansions. `type` is a native-Class assertion inside a selected Domain. It never selects or adds a Domain. The same `UWidgetBlueprint`, for example, has separate Blueprint and Widget Targets. Every Target Guid is non-zero canonical lowercase text with hyphens, matching UE `FGuid::IsValid()`. ## Stable References Contained-object references are interpreted only inside the active exact Target: ```sal @node-guid @node-guid/pin-guid @container-guid/property-guid ``` The path contains native identity components, not kind words, collection names, display names, or array positions. A Graph Pin includes its owning NodeGuid even when its PinId happens to be unique. A semantic tag may decorate the same reference: ```sal node @node-guid pin @node-guid/pin-guid ``` The tag is ignored for identity equality and native lookup. `@id`, `node @id`, and another allowed tag on the same identity address the same native object; their parsed ASTs may still retain different presentation metadata. Member paths follow identity: ```sal @node-guid/pin-guid.DefaultValue ``` The exact Target itself is structural: ```sal query g target with schema ``` A Target may also be a relationship subject, but only where the Domain exposes confirmed native declaration identity: ```sal query functionGraph references to target query interfaceFunctionGraph references to target.InterfaceGuid ``` It has no synthetic StableRef. Bare Target-self currently resolves only for callable Function/Macro Graph Targets; the direct `target.InterfaceGuid` member resolves for a valid Graph Interface declaration. Other Domain or Graph roles return `capability.reference_unavailable`. ## Query ```sal <alias> = target { domain: <domain>, ... } query <alias> [<one primary operation>] [where <condition>] [with <detail>, ...] [order by <field> [asc|desc], ...] [page limit <count>] [page after "<cursor>"] ``` The operation-less form is the shared exact-target read and normalizes to `target`. Contained exact reads use one StableRef: ```sal query g @node-guid with schema ``` Plural operations enumerate or search. Singular names are discovery only; copy the returned stable identity for later exact access. `references` finds factual authored use-sites. Each Domain card closes the allowed operations, clauses, fields, expansions, and pagination rules. ## Patch ```sal <alias> = target { domain: <domain>, ... } patch <alias> [dry run] <binding or operation> <binding or operation> ``` Core operations are: ```sal add <binding> [to <destination>|before <anchor>|after <anchor>] remove <object> set <object>.<field> = <value> reset <object>.<field> move <domain-defined operands> invoke <object> <Operation>(namedArguments) [as <alias>] save ``` Domain cards add operations such as Graph `connect`, Widget `wrap`, StateTree `bind`, and Blueprint or StateTree `compile`. Every directly created object starts with a Palette result: ```sal query g palette entries "Print String" ``` Copy its object fields into the Patch: ```sal patch g print = { palette: "P_PrintString" } add print ``` A formatter may emit `node { palette: "P_PrintString" }`, but the tag is erasable. The active Domain, operation, Palette identity, and destination provide the creation semantics. Never guess a Palette id, Class, field, Pin, or operation parameter. Patch statements execute in written order. Dry run follows the same parse, resolve, validate, and plan path, then stops before live application. Only Domains whose static card defines a Patch surface are Patch Targets; `level` admits authored Actor and Component field edits, `pcg` admits Palette-backed Node creation, Settings edits, movement, connection edits, removal, and a terminal `save`, and `pcg_component` admits certified scalar edits under its async edit guard. ## Results And Handoffs Result text begins with its Target context and an explicit Target table: ```sal result exact_target target g = target { domain: graph, asset: "/Game/BP_Door.BP_Door", blueprintId: "11111111-1111-1111-1111-111111111111", id: "22222222-2222-2222-2222-222222222222" } objects beginPlay = node { id: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", type: "/Script/BlueprintGraph.K2Node_Event" } ``` The three result contexts are `exact_target`, `domain_root`, and `unresolved_target`. Exact results always return the canonical Target after it opens, including later operation failures. Unresolved results contain no Target and at least one error diagnostic. The first MCP text block is always the canonical, round-trippable Result Text. If no Object Text exists, it ends with `no_objects`, which is a strict terminator. Mutation metadata and diagnostics use later independent text blocks formatted as SAL comments; they are not appended to Result Text and cannot fabricate an `objects` section. Cross-Domain navigation adds an independent related Target and explicit handoff: The following is a Result Text fragment, not a standalone Result Text document. ```sal related bp = target { domain: blueprint, asset: "/Game/BP_Door.BP_Door", id: "11111111-1111-1111-1111-111111111111" } handoff compile to bp ``` A related Target is never nested in an object. A reference relative to it is qualified as `bp::@identity`; unqualified references remain relative to the main exact Target. That scoped form belongs to results. Request text may use only its own active Target alias as a redundant qualifier, which the parser lowers away; a foreign or unknown qualifier is rejected. ## Schema Discovery Schema has three layers: 1. this resident guide; 2. one static Domain card from `sal_schema`; 3. exact, live `with schema` output for a Target, object, or Palette entry. ```sal query g target with schema query g @node-guid with schema ``` Exact schema is authoritative for fields, constraints, direct Patch statements, instance availability, operations, parameters, outputs, and copyable templates. Diagnostics should point back to static schema, an exact schema query, a fresh collection/tree query, or an explicit Target handoff. ## Compatibility Window Only callers that opt into the direct TypeScript parser's protocol-v3 compatibility mode may submit legacy object constructors, Domain Target constructors, or fused kind references. MCP tools and the default SDK facade do not enable it. The reader lowers only forms that are unambiguous in the active Domain. Under-scoped owner identities, target-self fused references, ambiguous or mixed Domains, and any form that would require UE-assisted recovery are rejected. The protocol-v6 Bridge rejects normalized legacy shapes and current formatters never emit them. This reader-only migration window remains a direct parser compatibility option; removal requires a later incompatible protocol and release note.
Input schema
{
  "type": "object",
  "properties": {
    "module": {
      "type": "string",
      "enum": [
        "asset",
        "blueprint",
        "class",
        "graph",
        "state_tree",
        "widget",
        "level",
        "pcg",
        "pcg_component"
      ],
      "description": "Optional interface module."
    }
  },
  "additionalProperties": false
}
Annotations
{
  "readOnlyHint": true,
  "destructiveHint": false,
  "idempotentHint": true,
  "openWorldHint": false
}
statusInspect Loomle Client and update status plus the bound session and Bridge health. Call once before the first Loomle operation in a task.
Input schema
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
Annotations
{
  "readOnlyHint": true,
  "destructiveHint": false,
  "idempotentHint": true,
  "openWorldHint": true
}

Resources 0

Resource templates 0

Prompts 0

Changes from previous version

Compared with initial baseline using full_baseline.

RiskChangeSubject
No material changes recorded.

Confirmed vulnerabilities

SeverityFindingAdvisory
No confirmed vulnerability is published for this version.

Provenance

Artifact SHA-256: cd0a87de646831a233b7fd3bac36934a4ec66623f25ca9f83332d72a2fc733ab

Scanner: mcp-proof-engine 0.1.0.

Let’s talk about MCP security.

Share your details and our security team will contact you.