diff --git a/build.gradle b/build.gradle
index 7bfebcfe84f3d6f53a6ceb997eb0108610135e31..c986f2a61adf610a9c2e5fdc7fd20eb193a9a46a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -69,19 +69,12 @@ task relast(type: JavaExec) {
     group = 'Build'
     main = "-jar"
 
-    doFirst {
-        delete "src/gen/jastadd/*.ast"
-        delete "src/gen/jastadd/RagConnect.jadd"
-        delete "src/gen/jastadd/RagConnectRefResolver.jadd"
-        delete "src/gen/jastadd/RagConnectResolverStubs.jrag"
-        mkdir  "src/gen/jastadd/"
-    }
-
     args = [
             "../libs/relast.jar",
             "../relast.preprocessor/src/main/jastadd/RelAst.relast",
             "./src/main/jastadd/RagConnect.relast",
             "./src/main/jastadd/MustacheNodes.relast",
+            "./src/main/jastadd/YAML.relast",
             "--listClass=java.util.ArrayList",
             "--jastAddList=JastAddList",
             "--useJastAddNames",
@@ -90,14 +83,15 @@ task relast(type: JavaExec) {
             "--grammarName=./src/gen/jastadd/RagConnect"
     ]
 
-    inputs.files file("../libs/relast.jar"),
-            file("../relast.preprocessor/src/main/jastadd/RelAST.relast"),
-            file("./src/main/jastadd/RagConnect.relast")
-            file("./src/main/jastadd/MustacheNodes.relast")
-    outputs.files file("./src/gen/jastadd/RagConnect.ast"),
-            file("./src/gen/jastadd/RagConnect.jadd"),
-            file("./src/gen/jastadd/RagConnectRefResolver.jadd"),
-            file('./src/gen/jastadd/RagConnectResolverStubs.jrag')
+    inputs.files("../libs/relast.jar",
+            "../relast.preprocessor/src/main/jastadd/RelAst.relast",
+            "./src/main/jastadd/RagConnect.relast",
+            "./src/main/jastadd/MustacheNodes.relast",
+            "./src/main/jastadd/YAML.relast")
+    outputs.files("./src/gen/jastadd/RagConnect.ast",
+            "./src/gen/jastadd/RagConnect.jadd",
+            "./src/gen/jastadd/RagConnectRefResolver.jadd",
+            './src/gen/jastadd/RagConnectResolverStubs.jrag')
 }
 
 jastadd {
diff --git a/src/main/jastadd/Analysis.jrag b/src/main/jastadd/Analysis.jrag
index 623a3f0ad8c0457d8d0606bf70dea0adfea89ea4..b62399960f34a4b8c23a8d23762ec195491ce4e3 100644
--- a/src/main/jastadd/Analysis.jrag
+++ b/src/main/jastadd/Analysis.jrag
@@ -1,13 +1,15 @@
 aspect Analysis {
   // --- lookupTokenEndpointDefinition ---
-  inh TokenEndpointDefinition TokenEndpointDefinition.lookupTokenEndpointDefinition(TokenComponent token);
-  eq RagConnect.getEndpointDefinition().lookupTokenEndpointDefinition(TokenComponent token) {
+  inh java.util.List<TokenEndpointDefinition> TokenEndpointDefinition.lookupTokenEndpointDefinitions(TokenComponent token);
+  eq RagConnect.getEndpointDefinition().lookupTokenEndpointDefinitions(TokenComponent token) = lookupTokenEndpointDefinitions(token);
+  syn java.util.List<TokenEndpointDefinition> RagConnect.lookupTokenEndpointDefinitions(TokenComponent token) {
+    java.util.List<TokenEndpointDefinition> result = new java.util.ArrayList<>();
     for (EndpointDefinition def : getEndpointDefinitionList()) {
       if (def.isTokenEndpointDefinition() && def.asTokenEndpointDefinition().getToken().equals(token)) {
-        return def.asTokenEndpointDefinition();
+        result.add(def.asTokenEndpointDefinition());
       }
     }
-    return null;
+    return result;
   }
 
   // --- lookupDependencyDefinition ---
@@ -22,6 +24,34 @@ aspect Analysis {
   }
 
   // --- isAlreadyDefined ---
-  syn boolean TokenEndpointDefinition.isAlreadyDefined() = lookupTokenEndpointDefinition(getToken()) != this;
+  syn boolean TokenEndpointDefinition.isAlreadyDefined() = lookupTokenEndpointDefinitions(getToken()).size() > 1;
   syn boolean DependencyDefinition.isAlreadyDefined() = lookupDependencyDefinition(getSource().containingTypeDecl(), getID()) != this;
+
+  syn boolean MappingDefinitionType.assignableTo(JavaTypeUse target);
+  eq JavaMappingDefinitionType.assignableTo(JavaTypeUse target) = getType().assignableTo(target);
+  eq JavaArrayMappingDefinitionType.assignableTo(JavaTypeUse target) {
+    if (!target.getName().endsWith("[]")) { return false; }
+    return getType().assignableTo(new SimpleJavaTypeUse(target.getName().replace("[]", "")));
+  }
+  syn boolean JavaTypeUse.assignableTo(JavaTypeUse target) {
+    // target var = this;
+    return target.primitivePrettyPrint().equals(this.primitivePrettyPrint());
+  }
+  syn String JavaTypeUse.primitivePrettyPrint() {
+    switch(getName()) {
+      case "int":
+      case "Integer": return "int";
+      case "short":
+      case "Short": return "short";
+      case "long":
+      case "Long": return "long";
+      case "float":
+      case "Float": return "float";
+      case "double":
+      case "Double": return "double";
+      case "char":
+      case "Character": return "char";
+      default: return getName();
+    }
+  }
 }
diff --git a/src/main/jastadd/Errors.jrag b/src/main/jastadd/Errors.jrag
index bfee2545d4bee28223b04ce931ccd6a6429cf13f..fe2fcd59efd365174ca2803affebc34819cf3d7e 100644
--- a/src/main/jastadd/Errors.jrag
+++ b/src/main/jastadd/Errors.jrag
@@ -7,32 +7,32 @@ aspect Errors {
     [new TreeSet<ErrorMessage>()]
     root RagConnect;
 
-    ReceiveFromMqttDefinition contributes error("Receive definition already defined for " + getToken().getName())
+    ReceiveTokenEndpointDefinition contributes error("Receive definition already defined for " + getToken().getName())
       when isAlreadyDefined()
       to RagConnect.errors();
 
-    ReceiveFromMqttDefinition contributes error("Receiving target token must not be an NTA token!")
+    ReceiveTokenEndpointDefinition contributes error("Receiving target token must not be an NTA token!")
       when getToken().getNTA()
       to RagConnect.errors();
 
     // if first mapping is null, then suitableDefaultMapping() == null
-    ReceiveFromMqttDefinition contributes error("No suitable default mapping found for type " +
+    ReceiveTokenEndpointDefinition contributes error("No suitable default mapping found for type " +
       ((getMappingList().isEmpty())
-        ? getToken().getJavaTypeUse().prettyPrint()
+        ? getToken().effectiveJavaTypeUse().prettyPrint()
         : getMappingList().get(0).getFromType().prettyPrint()))
       when effectiveMappings().get(0) == null
       to RagConnect.errors();
 
-    ReceiveFromMqttDefinition contributes error("to-type of last mapping must be type of the Token!")
-      when getToken().getJavaTypeUse().prettyPrint().equals(
-          effectiveMappings().get(effectiveMappings().size() - 1))
+    ReceiveTokenEndpointDefinition contributes error("to-type of last mapping (" + effectiveMappings().get(effectiveMappings().size() - 1).getToType().prettyPrint() + ") not assignable to type of the Token (" + getToken().effectiveJavaTypeUse().prettyPrint() + ")!")
+      when !effectiveMappings().get(effectiveMappings().size() - 1).getToType().assignableTo(
+          getToken().effectiveJavaTypeUse())
       to RagConnect.errors();
 
-    SendToMqttDefinition contributes error("Sending target token must be an NTA token!")
+    SendTokenEndpointDefinition contributes error("Sending target token must be an NTA token!")
       when !getToken().getNTA()
       to RagConnect.errors();
 
-    SendToMqttDefinition contributes error("Send definition already defined for " + getToken().getName())
+    SendTokenEndpointDefinition contributes error("Send definition already defined for " + getToken().getName())
       when isAlreadyDefined()
       to RagConnect.errors();
 
@@ -44,7 +44,7 @@ aspect Errors {
       when isAlreadyDefinedAsList()
       to RagConnect.errors();
 
-    DependencyDefinition contributes error("There must be a send update definition targeting " + getSource().parentTypeypeAndName() + " for dependency definition " + getID())
+    DependencyDefinition contributes error("There must be a send endpoint definition targeting " + getSource().parentTypeypeAndName() + " for dependency definition " + getID())
       when targetEndpointDefinition() == null
       to RagConnect.errors();
 }
diff --git a/src/main/jastadd/MustacheNodes.relast b/src/main/jastadd/MustacheNodes.relast
index ce85caba554d1f582079b299f457d7675ab12dfa..62ecdb6166a783e125cefef2826d550b73ab9e1b 100644
--- a/src/main/jastadd/MustacheNodes.relast
+++ b/src/main/jastadd/MustacheNodes.relast
@@ -1,6 +1,3 @@
-//TypeComponentMustache ;
-//rel TypeComponentMustache.TypeComponent -> TypeComponent ;
-
 MRagConnect ::= ReceiveDefinition:MReceiveDefinition* SendDefinition:MSendDefinition* MappingDefinition:MMappingDefinition* DependencyDefinition:MDependencyDefinition* RootTypeComponent:MTypeComponent* TokenComponent:MTokenComponent*;
 abstract MEndpointDefinition ::= InnerMappingDefinition:MInnerMappingDefinition*;
 MReceiveDefinition : MEndpointDefinition;
@@ -12,9 +9,9 @@ MTypeComponent;
 MTokenComponent;
 
 rel MRagConnect.RagConnect -> RagConnect;
-rel MInnerMappingDefinition.MappingDefinition -> MappingDefinition;
-rel MReceiveDefinition.ReceiveFromMqttDefinition -> ReceiveFromMqttDefinition;
-rel MSendDefinition.SendToMqttDefinition -> SendToMqttDefinition;
+rel MInnerMappingDefinition.MMappingDefinition -> MMappingDefinition;
+rel MReceiveDefinition.ReceiveTokenEndpointDefinition -> ReceiveTokenEndpointDefinition;
+rel MSendDefinition.SendTokenEndpointDefinition -> SendTokenEndpointDefinition;
 rel MMappingDefinition.MappingDefinition -> MappingDefinition;
 rel MDependencyDefinition.DependencyDefinition -> DependencyDefinition;
 rel MTypeComponent.TypeComponent -> TypeComponent;
diff --git a/src/main/jastadd/Navigation.jrag b/src/main/jastadd/Navigation.jrag
index 2314386eb9f74ecb5699d6fba91956e80f75fa63..752fc36aea4c59348874c1a08134696a9543b6cb 100644
--- a/src/main/jastadd/Navigation.jrag
+++ b/src/main/jastadd/Navigation.jrag
@@ -10,9 +10,12 @@ aspect Navigation {
   eq MRagConnect.getChild().ragconnect() = getRagConnect();
 
   // --- containedFile
+  eq Grammar.getChild().containedFile() = null;
   eq RagConnect.getChild().containedFile() = null;
+  eq MRagConnect.getChild().containedFile() = null;
 
   // --- containedFileName ---
+  eq Grammar.getChild().containedFileName() = null;  // should be in PP
   eq RagConnect.getChild().containedFileName() = getFileName();
   eq MRagConnect.getChild().containedFileName() = null;
 
@@ -24,30 +27,33 @@ aspect Navigation {
   syn TokenEndpointDefinition EndpointDefinition.asTokenEndpointDefinition() = null;
   eq TokenEndpointDefinition.asTokenEndpointDefinition() = this;
 
-  // --- isSendToMqttDefinition ---
-  syn boolean EndpointDefinition.isSendToMqttDefinition() = false;
-  eq SendToMqttDefinition.isSendToMqttDefinition() = true;
+  // --- isSendTokenEndpointDefinition ---
+  syn boolean EndpointDefinition.isSendTokenEndpointDefinition() = false;
+  eq SendTokenEndpointDefinition.isSendTokenEndpointDefinition() = true;
 
-  // --- asSendToMqttDefinition ---
-  syn SendToMqttDefinition EndpointDefinition.asSendToMqttDefinition() = null;
-  eq SendToMqttDefinition.asSendToMqttDefinition() = this;
+  // --- asSendTokenEndpointDefinition ---
+  syn SendTokenEndpointDefinition EndpointDefinition.asSendTokenEndpointDefinition() = null;
+  eq SendTokenEndpointDefinition.asSendTokenEndpointDefinition() = this;
 
-  // --- asReceiveFromMqttDefinition ---
-  syn ReceiveFromMqttDefinition EndpointDefinition.asReceiveFromMqttDefinition() = null;
-  eq ReceiveFromMqttDefinition.asReceiveFromMqttDefinition() = this;
+  // --- asReceiveTokenEndpointDefinition ---
+  syn ReceiveTokenEndpointDefinition EndpointDefinition.asReceiveTokenEndpointDefinition() = null;
+  eq ReceiveTokenEndpointDefinition.asReceiveTokenEndpointDefinition() = this;
 
   // --- targetEndpointDefinition ---
-  syn SendToMqttDefinition DependencyDefinition.targetEndpointDefinition() {
+  syn SendTokenEndpointDefinition DependencyDefinition.targetEndpointDefinition() {
     // resolve definition in here, as we do not need resolveMethod in any other place (yet)
     for (EndpointDefinition endpointDefinition : ragconnect().getEndpointDefinitionList()) {
-      if (endpointDefinition.isSendToMqttDefinition() &&
-          endpointDefinition.asSendToMqttDefinition().getToken().equals(this.getTarget())) {
-        return endpointDefinition.asSendToMqttDefinition();
+      if (endpointDefinition.isSendTokenEndpointDefinition() &&
+          endpointDefinition.asSendTokenEndpointDefinition().getToken().equals(this.getTarget())) {
+        return endpointDefinition.asSendTokenEndpointDefinition();
       }
     }
     return null;
   }
 
+  // --- effectiveJavaTypeUse (should be in preprocessor) ---
+  syn lazy JavaTypeUse TokenComponent.effectiveJavaTypeUse() = hasJavaTypeUse() ? getJavaTypeUse() : new SimpleJavaTypeUse("String");
+
   // --- isDefaultMappingDefinition ---
   syn boolean MappingDefinition.isDefaultMappingDefinition() = false;
   eq DefaultMappingDefinition.isDefaultMappingDefinition() = true;
diff --git a/src/main/jastadd/Printing.jrag b/src/main/jastadd/Printing.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..74607aeb7737e1e43c9565b799693889a648a9df
--- /dev/null
+++ b/src/main/jastadd/Printing.jrag
@@ -0,0 +1,75 @@
+aspect Printing {
+  String ASTNode.PRINT_INDENT = "  ";
+
+  syn String MappingDefinitionType.prettyPrint();
+  eq JavaMappingDefinitionType.prettyPrint() = getType().getName();
+  eq JavaArrayMappingDefinitionType.prettyPrint() = getType().getName() + "[]";
+
+  syn String JavaTypeUse.prettyPrint() {
+    StringBuilder sb = new StringBuilder();
+    generateAbstractGrammar(sb);
+    return sb.toString();
+  }
+
+  syn String Document.prettyPrint() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("# RagConnect created at ").append(java.time.Instant.now()).append("\n");
+    for (ComplexElement element : getComplexElementList()) {
+      element.prettyPrint(sb, false, "");
+    }
+    if (sb.charAt(sb.length() - 1) != '\n') {
+      sb.append("\n");
+    }
+    return sb.toString();
+  }
+  syn StringBuilder Element.prettyPrint(StringBuilder sb, boolean printIndent, String indent);
+  eq ValueElement.prettyPrint(StringBuilder sb, boolean printIndent, String indent) {
+    sb.append(getValue());
+    return sb;
+  }
+  eq StringElement.prettyPrint(StringBuilder sb, boolean printIndent, String indent) {
+    sb.append("\"").append(getValue()).append("\"");
+    return sb;
+  }
+  eq ListElement.prettyPrint(StringBuilder sb, boolean printIndent, String indent) {
+    if (isEmpty()) {
+      sb.append("[]");
+    } else {
+      for (Element element : getElementList()) {
+        sb.append(indent).append("- ");
+        element.prettyPrint(sb, false, indent + PRINT_INDENT);
+        sb.append("\n");
+      }
+      // delete last newline
+      sb.deleteCharAt(sb.length() - 1);
+    }
+    return sb;
+  }
+  eq KeyValuePair.prettyPrint(StringBuilder sb, boolean printIndent, String indent) {
+    if (printIndent) sb.append(indent);
+    sb.append(getKey()).append(": ");
+    if (getValue().isComplex() && !getValue().isEmpty()) {
+      sb.append("\n");
+      getValue().prettyPrint(sb, true, indent + PRINT_INDENT);  //);
+    } else {
+      getValue().prettyPrint(sb, false, indent);
+    }
+    return sb;
+  }
+  eq MappingElement.prettyPrint(StringBuilder sb, boolean printIndent, String indent) {
+    if (isEmpty()) {
+      sb.append("{}");
+    } else {
+      boolean first = true;
+      for (KeyValuePair pair : getKeyValuePairList()) {
+        if (!first || printIndent) sb.append(indent);
+        first = false;
+        pair.prettyPrint(sb, false, indent);  // + PRINT_INDENT
+        sb.append("\n");
+      }
+      // delete last newline
+      sb.deleteCharAt(sb.length() - 1);
+    }
+    return sb;
+  }
+}
diff --git a/src/main/jastadd/RagConnect.relast b/src/main/jastadd/RagConnect.relast
index d30116efae1a6735e88922e31f42f99911c6532a..accc617714ff6bdd24a22831ea84e5d93e6a15b8 100644
--- a/src/main/jastadd/RagConnect.relast
+++ b/src/main/jastadd/RagConnect.relast
@@ -2,13 +2,13 @@ RagConnect ::= EndpointDefinition* DependencyDefinition* MappingDefinition* Prog
 
 abstract EndpointDefinition ::= <AlwaysApply:boolean> ;
 
-rel EndpointDefinition.Mapping* -> MappingDefinition;
+rel EndpointDefinition.Mapping* <-> MappingDefinition.UsedAt*;
 
 abstract TokenEndpointDefinition : EndpointDefinition;
 rel TokenEndpointDefinition.Token -> TokenComponent;
 
-ReceiveFromMqttDefinition : TokenEndpointDefinition;
-SendToMqttDefinition  : TokenEndpointDefinition;
+ReceiveTokenEndpointDefinition : TokenEndpointDefinition;
+SendTokenEndpointDefinition : TokenEndpointDefinition;
 
 DependencyDefinition ::= <ID>;
 rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*;
diff --git a/src/main/jastadd/Util.jadd b/src/main/jastadd/Util.jadd
new file mode 100644
index 0000000000000000000000000000000000000000..3b59b13c53a8d0e1ca2a05feba43294797a4577e
--- /dev/null
+++ b/src/main/jastadd/Util.jadd
@@ -0,0 +1,5 @@
+aspect Util {
+  static String ASTNode.capitalize(String s) {
+    return Character.toUpperCase(s.charAt(0)) + s.substring(1);
+  }
+}
diff --git a/src/main/jastadd/YAML.jrag b/src/main/jastadd/YAML.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..93dd0f34b7b7b0505cf0970b6541fa3ae5bee7fa
--- /dev/null
+++ b/src/main/jastadd/YAML.jrag
@@ -0,0 +1,31 @@
+aspect Navigation {
+  eq Document.getChild().program() = null;
+  eq Document.getChild().ragconnect() = null;
+  eq Document.getChild().containedFile() = null;
+  eq Document.getChild().containedFileName() = getFileName();
+
+  syn boolean Element.isComplex() = false;
+  eq ComplexElement.isComplex() = true;
+
+  syn boolean Element.isEmpty() = false;
+  eq MappingElement.isEmpty() = getNumKeyValuePair() == 0;
+  eq ListElement.isEmpty() = getNumElement() == 0;
+}
+
+aspect Helper {
+  public static ValueElement ValueElement.of(int value) {
+    return new ValueElement(String.valueOf(value));
+  }
+  public static ValueElement ValueElement.of(boolean value) {
+    return new ValueElement(String.valueOf(value));
+  }
+  public static ValueElement ValueElement.of(String value) {
+    return new ValueElement(value);
+  }
+  public static StringElement StringElement.of(String value) {
+    return new StringElement(value);
+  }
+  public void MappingElement.addKeyValuePair(String key, Element value) {
+    addKeyValuePair(new KeyValuePair(key, value));
+  }
+}
diff --git a/src/main/jastadd/YAML.relast b/src/main/jastadd/YAML.relast
new file mode 100644
index 0000000000000000000000000000000000000000..94f4ab20fdc3838348bd53b812b3d1477cae3558
--- /dev/null
+++ b/src/main/jastadd/YAML.relast
@@ -0,0 +1,9 @@
+Document ::= <FileName> ComplexElement* ;
+abstract Element ;
+abstract ComplexElement : Element ;
+MappingElement : ComplexElement ::= KeyValuePair* ;
+KeyValuePair : ComplexElement ::= <Key> Value:Element ;
+ListElement : ComplexElement ::= Element* ;
+abstract SimpleElement : Element ;
+ValueElement : SimpleElement ::= <Value> ;
+StringElement : SimpleElement ::= <Value> ;
diff --git a/src/main/jastadd/backend/Configuration.jadd b/src/main/jastadd/backend/Configuration.jadd
index d4f5b43f27cb329f1ff82318c6f383f0f69ebbd6..8d3b16c3f99014408c6aefa5655201d222c3d892 100644
--- a/src/main/jastadd/backend/Configuration.jadd
+++ b/src/main/jastadd/backend/Configuration.jadd
@@ -2,4 +2,6 @@ aspect Configuration {
   public static boolean ASTNode.loggingEnabledForReads = false;
   public static boolean ASTNode.loggingEnabledForWrites = false;
   public static TypeDecl ASTNode.rootNode;
+  public static boolean ASTNode.usesMqtt;
+  public static boolean ASTNode.usesRest;
 }
diff --git a/src/main/jastadd/backend/Generation.jadd b/src/main/jastadd/backend/Generation.jadd
index 067626ed42f12add896ca4eea7c65917309c0e2e..4b8d357e82e82eb8454451978589434f1ef44505 100644
--- a/src/main/jastadd/backend/Generation.jadd
+++ b/src/main/jastadd/backend/Generation.jadd
@@ -1,41 +1,19 @@
-aspect GenerationUtils {
-  public static final String ASTNode.aspectIndent = "  ";
-
-  public String ASTNode.ind(int n) {
-    StringBuilder s = new StringBuilder();
-    for (int i = 0; i < n; i++) {
-      s.append(aspectIndent);
-    }
-    return s.toString();
-  }
-
-  // --- prettyPrint ---
-  syn String MappingDefinitionType.prettyPrint();
-  eq JavaMappingDefinitionType.prettyPrint() = getType().getName();
-  eq JavaArrayMappingDefinitionType.prettyPrint() = getType().getName() + "[]";
-
-  syn String JavaTypeUse.prettyPrint() {
-    StringBuilder sb = new StringBuilder();
-    generateAbstractGrammar(sb);
-    return sb.toString();
-  }
-}
-
-/* Open questions
-- Should all string constants be defined on the normal AST, or on the special mustache AST?
+/*
+Design considerations
+- InnerMappingDefinition needed for iteration attribute (first, last) - not easily possible with list-relation
 */
 
 aspect AttributesForMustache {
   // --- MRagConnect ---
-  eq MRagConnect.getChild().mqttHandlerAttribute() = mqttHandlerAttribute();
-  eq MRagConnect.getChild().mqttHandlerField() = mqttHandlerField();
   eq MRagConnect.getRootTypeComponent(int i).isFirst() = i == 0;
 
-  syn String MRagConnect.mqttHandlerAttribute() = getRagConnect().mqttHandlerAttribute();
-  syn String MRagConnect.mqttHandlerField() = getRagConnect().mqttHandlerField();
-  syn String MRagConnect.mqttSetHostMethod() = getRagConnect().mqttSetHostMethod();
-  syn String MRagConnect.mqttWaitUntilReadyMethod() = getRagConnect().mqttWaitUntilReadyMethod();
-  syn String MRagConnect.mqttCloseMethod() = getRagConnect().mqttCloseMethod();
+  syn String MRagConnect.closeMethod() = "ragconnectCloseConnections";
+  syn String MRagConnect.mqttHandlerAttribute() = "_mqttHandler";
+  syn String MRagConnect.mqttHandlerField() = "_mqttHandler";
+  syn String MRagConnect.mqttSetupWaitUntilReadyMethod() = "ragconnectSetupMqttWaitUntilReady";
+
+  syn String MRagConnect.restHandlerAttribute() = "_restHandler";
+  syn String MRagConnect.restHandlerField() = "_restHandler";
 
   // --- MEndpointDefinition ---
   syn String MEndpointDefinition.preemptiveExpectedValue();
@@ -44,29 +22,25 @@ aspect AttributesForMustache {
   syn String MEndpointDefinition.firstInputVarName();
 
   eq MEndpointDefinition.getInnerMappingDefinition(int i).isLast() = i == getNumInnerMappingDefinition() - 1;
-  eq MEndpointDefinition.getInnerMappingDefinition().resultVarPrefix() = resultVarPrefix();
-  eq MEndpointDefinition.getInnerMappingDefinition(int i).inputVarName() = i == 0 ? firstInputVarName() : resultVarPrefix() + getInnerMappingDefinition(i - 1).getMappingDefinition().methodName();
-
-  inh String MEndpointDefinition.mqttHandlerAttribute();
+  eq MEndpointDefinition.getInnerMappingDefinition(int i).inputVarName() = i == 0 ? firstInputVarName() : getInnerMappingDefinition(i - 1).outputVarName();
 
-  syn String MEndpointDefinition.connectMethod() = endpointDef().connectMethod();
+  syn String MEndpointDefinition.connectParameterName() = "uriString";
+  syn String MEndpointDefinition.connectMethod() = "connect" + tokenName();
   syn TokenComponent MEndpointDefinition.token() = endpointDef().getToken();
   syn boolean MEndpointDefinition.alwaysApply() = endpointDef().getAlwaysApply();
-  syn String MEndpointDefinition.resultVarPrefix() = "result";  // we do not need "_" here, because methodName begins with one
   syn String MEndpointDefinition.parentTypeName() = token().containingTypeDecl().getName();
   syn String MEndpointDefinition.tokenName() = token().getName();
   syn MInnerMappingDefinition MEndpointDefinition.lastDefinition() = getInnerMappingDefinition(getNumInnerMappingDefinition() - 1);
-  syn String MEndpointDefinition.lastDefinitionToType() = lastDefinition().ToType();
-  syn String MEndpointDefinition.lastDefinitionName() = lastDefinition().methodName();
-  syn String MEndpointDefinition.lastResult() = resultVarPrefix() + lastDefinitionName();
+  syn String MEndpointDefinition.lastDefinitionToType() = lastDefinition().toType();
+  syn String MEndpointDefinition.lastResult() = lastDefinition().outputVarName();
   syn String MEndpointDefinition.condition() {
-    if (lastDefinition().getMappingDefinition().getToType().isArray()) {
+    if (lastDefinition().mappingDef().getToType().isArray()) {
       return "java.util.Arrays.equals(" + preemptiveExpectedValue() + ", " + lastResult() + ")";
     }
-    if (token().isPrimitiveType() && lastDefinition().getMappingDefinition().getToType().isPrimitiveType()) {
+    if (token().isPrimitiveType() && lastDefinition().mappingDef().getToType().isPrimitiveType()) {
       return preemptiveExpectedValue() + " == " + lastResult();
     }
-    if (lastDefinition().getMappingDefinition().isDefaultMappingDefinition()) {
+    if (lastDefinition().mappingDef().isDefaultMappingDefinition()) {
       return preemptiveExpectedValue() + " != null && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")";
     }
     return preemptiveExpectedValue() + " != null ? " + preemptiveExpectedValue() + ".equals(" + lastResult() + ") : " + lastResult() + " == null";
@@ -74,66 +48,67 @@ aspect AttributesForMustache {
 
   // --- MInnerMappingDefinition ---
   inh boolean MInnerMappingDefinition.isLast();
-  inh String MInnerMappingDefinition.resultVarPrefix();
-  syn String MInnerMappingDefinition.ToType() = getMappingDefinition().getToType().prettyPrint();
-  syn String MInnerMappingDefinition.methodName() = getMappingDefinition().methodName();
   inh String MInnerMappingDefinition.inputVarName();
+  syn String MInnerMappingDefinition.toType() = mappingDef().getToType().prettyPrint();
+  syn String MInnerMappingDefinition.methodName() = getMMappingDefinition().methodName();
+  syn MappingDefinition MInnerMappingDefinition.mappingDef() = getMMappingDefinition().getMappingDefinition();
+  syn String MInnerMappingDefinition.outputVarName() = "result" + methodName();  // we do not need "_" in between here, because methodName begins with one
 
   // --- MReceiveDefinition ---
   eq MReceiveDefinition.preemptiveExpectedValue() = "get" + tokenName() + "()";
   eq MReceiveDefinition.preemptiveReturn() = "return;";
-  eq MReceiveDefinition.endpointDef() = getReceiveFromMqttDefinition();
+  eq MReceiveDefinition.endpointDef() = getReceiveTokenEndpointDefinition();
   eq MReceiveDefinition.firstInputVarName() = "message";
 
   // --- MSendDefinition ---
   eq MSendDefinition.preemptiveExpectedValue() = lastValue();
   eq MSendDefinition.preemptiveReturn() = "return false;";
-  eq MSendDefinition.endpointDef() = getSendToMqttDefinition();
+  eq MSendDefinition.endpointDef() = getSendTokenEndpointDefinition();
   eq MSendDefinition.firstInputVarName() = "get" + tokenName() + "()";
 
-  syn String MSendDefinition.sendTopic() = getSendToMqttDefinition().sendTopic();
-  syn String MSendDefinition.lastValue() = getSendToMqttDefinition().lastValue();
-  syn String MSendDefinition.updateMethod() = getSendToMqttDefinition().updateMethod();
-  syn String MSendDefinition.writeMethod() = getSendToMqttDefinition().writeMethod();
-  syn String MSendDefinition.tokenResetMethod() = getSendToMqttDefinition().tokenResetMethod();
+  syn String MSendDefinition.sender() = "_sender_" + tokenName();
+  syn String MSendDefinition.lastValue() = "_lastValue" + tokenName();
+  syn String MSendDefinition.updateMethod() = "_update_" + tokenName();
+  syn String MSendDefinition.writeMethod() = "_writeLastValue_" + tokenName();
+  syn String MSendDefinition.tokenResetMethod() = "get" + tokenName() + "_reset";
 
   // --- MMappingDefinition ---
   syn String MMappingDefinition.toType() = getMappingDefinition().getToType().prettyPrint();
-  syn String MMappingDefinition.methodName() = getMappingDefinition().methodName();
+  syn String MMappingDefinition.methodName() = "_apply_" + getMappingDefinition().getID();
   syn String MMappingDefinition.fromType() = getMappingDefinition().getFromType().prettyPrint();
   syn String MMappingDefinition.fromVariableName() = getMappingDefinition().getFromVariableName();
   syn String MMappingDefinition.content() = getMappingDefinition().getContent();
 
   // --- MDependencyDefinition ---
   syn String MDependencyDefinition.targetParentTypeName() = getDependencyDefinition().getTarget().containingTypeDecl().getName();
-  syn String MDependencyDefinition.dependencyMethod() = getDependencyDefinition().dependencyMethod();
+  syn String MDependencyDefinition.dependencyMethod() = "add" + capitalize(getDependencyDefinition().getID());
   syn String MDependencyDefinition.sourceParentTypeName() = getDependencyDefinition().getSource().containingTypeDecl().getName();
-  syn String MDependencyDefinition.internalRelationPrefix() = getDependencyDefinition().internalRelationPrefix();
-  syn nta MEndpointDefinition MDependencyDefinition.targetEndpointDefinition() {
+  syn String MDependencyDefinition.internalRelationPrefix() = "_internal_" + getDependencyDefinition().getID();
+  syn nta MSendDefinition MDependencyDefinition.targetEndpointDefinition() {
     return getDependencyDefinition().targetEndpointDefinition().toMustache();
   }
 
   // --- MTypeComponent ---
   syn String MTypeComponent.name() = getTypeComponent().getName();
-  inh String MTypeComponent.mqttHandlerAttribute();
-  inh String MTypeComponent.mqttHandlerField();
   inh boolean MTypeComponent.isFirst();
 
   // --- MTokenComponent ---
   syn String MTokenComponent.parentTypeName() = getTokenComponent().containingTypeDecl().getName();
   syn String MTokenComponent.name() = getTokenComponent().getName();
-  syn String MTokenComponent.javaType() = getTokenComponent().getJavaTypeUse().prettyPrint();
-  syn String MTokenComponent.internalName() = getTokenComponent().internalName();
+  syn String MTokenComponent.javaType() = getTokenComponent().effectiveJavaTypeUse().prettyPrint();
+  syn String MTokenComponent.internalName() = getTokenComponent().getDependencySourceDefinitionList().isEmpty() ? externalName() : "_internal_" + name();
+  syn String MTokenComponent.externalName() = name();
 
   // --- toMustache ---
   syn lazy MRagConnect RagConnect.toMustache() {
     MRagConnect result = new MRagConnect();
     result.setRagConnect(this);
     for (EndpointDefinition def : getEndpointDefinitionList()) {
-      if (def.isSendToMqttDefinition()) {
-        result.addSendDefinition(def.asSendToMqttDefinition().toMustache());
+      if (def.isSendTokenEndpointDefinition()) {
+        SendTokenEndpointDefinition sendDef = def.asSendTokenEndpointDefinition();
+        result.addSendDefinition(sendDef.toMustache());
       } else {
-        result.addReceiveDefinition(def.asReceiveFromMqttDefinition().toMustache());
+        result.addReceiveDefinition(def.asReceiveTokenEndpointDefinition().toMustache());
       }
     }
     for (MappingDefinition def : allMappingDefinitions()) {
@@ -155,41 +130,46 @@ aspect AttributesForMustache {
     return result;
   }
 
-//MInnerMappingDefinition.MappingDefinition -> MappingDefinition;
   protected void MEndpointDefinition.addInnerMappings() {
     for (MappingDefinition def : endpointDef().effectiveMappings()) {
       MInnerMappingDefinition inner = new MInnerMappingDefinition();
-      inner.setMappingDefinition(def);
+      inner.setMMappingDefinition(def.toMustache());
       addInnerMappingDefinition(inner);
     }
   }
-  syn lazy MReceiveDefinition ReceiveFromMqttDefinition.toMustache() {
+
+  syn lazy MReceiveDefinition ReceiveTokenEndpointDefinition.toMustache() {
     MReceiveDefinition result = new MReceiveDefinition();
-    result.setReceiveFromMqttDefinition(this);
+    result.setReceiveTokenEndpointDefinition(this);
     result.addInnerMappings();
     return result;
   }
-  syn lazy MSendDefinition SendToMqttDefinition.toMustache() {
+
+  syn lazy MSendDefinition SendTokenEndpointDefinition.toMustache() {
     MSendDefinition result = new MSendDefinition();
-    result.setSendToMqttDefinition(this);
+    result.setSendTokenEndpointDefinition(this);
     result.addInnerMappings();
     return result;
   }
+
   syn lazy MMappingDefinition MappingDefinition.toMustache() {
     MMappingDefinition result = new MMappingDefinition();
     result.setMappingDefinition(this);
     return result;
   }
+
   syn lazy MDependencyDefinition DependencyDefinition.toMustache() {
     MDependencyDefinition result = new MDependencyDefinition();
     result.setDependencyDefinition(this);
     return result;
   }
+
   syn lazy MTypeComponent TypeComponent.toMustache() {
     MTypeComponent result = new MTypeComponent();
     result.setTypeComponent(this);
     return result;
   }
+
   syn lazy MTokenComponent TokenComponent.toMustache() {
     MTokenComponent result = new MTokenComponent();
     result.setTokenComponent(this);
@@ -201,37 +181,6 @@ aspect AttributesForMustache {
 }
 
 aspect AspectGeneration {
-  // naming convention attributes
-  syn String TokenComponent.internalName() = getDependencySourceDefinitionList().isEmpty() ? externalName() : "_internal_" + getName();
-  syn String TokenComponent.externalName() = getName();
-
-  syn String TokenEndpointDefinition.connectMethod() = "connect" + getToken().getName();
-  syn String SendToMqttDefinition.sendTopic() = "_topic_" + getToken().getName();
-  syn String SendToMqttDefinition.lastValue() = "_lastValue" + getToken().getName();
-  syn String SendToMqttDefinition.updateMethod() = "_update_" + getToken().getName();
-  syn String SendToMqttDefinition.writeMethod() = "_writeLastValue_" + getToken().getName();
-  syn String SendToMqttDefinition.tokenResetMethod() = "get" + getToken().getName() + "_reset";
-  syn String MappingDefinition.methodName() = "_apply_" + getID();
-  syn String DependencyDefinition.dependencyMethod() = "add" +
-    Character.toUpperCase(getID().charAt(0)) +
-    getID().substring(1);
-  syn String DependencyDefinition.internalRelationPrefix() = "_internal_" + getID();
-  syn String DependencyDefinition.internalTokenName() = getSource().internalName();
-
-  syn String RagConnect.mqttHandlerAttribute() = "_mqttHandler";
-  syn String RagConnect.mqttHandlerField() = "_mqttHandler";
-
-  syn String RagConnect.mqttSetHostMethod() = "MqttSetHost";
-  syn String RagConnect.mqttWaitUntilReadyMethod() = "MqttWaitUntilReady";
-  syn String RagConnect.mqttCloseMethod() = "MqttCloseConnections";
-
-  // naming copy attributes
-  // --- mqttHandlerAttribute ---
-  inh String EndpointDefinition.mqttHandlerAttribute();
-  inh String MappingDefinition.mqttHandlerAttribute();
-  inh String DependencyDefinition.mqttHandlerAttribute();
-  eq RagConnect.getChild().mqttHandlerAttribute() = mqttHandlerAttribute();
-
   // --- rootNodeName ---
   syn String ASTNode.rootNodeName() = rootNode.getName();
 
@@ -275,10 +224,11 @@ aspect RelationGeneration {
   }
 
   syn nta Relation DependencyDefinition.getRelationToCreate() {
+    String internalRelationPrefix = toMustache().internalRelationPrefix();
     BidirectionalRelation result = new BidirectionalRelation();
-    NavigableRole left = new ListRole(internalRelationPrefix() + "Source");
+    NavigableRole left = new ListRole(internalRelationPrefix + "Source");
     left.setType(getTarget().containingTypeDecl());
-    NavigableRole right = new ListRole(internalRelationPrefix() + "Target");
+    NavigableRole right = new ListRole(internalRelationPrefix + "Target");
     right.setType(getSource().containingTypeDecl());
     result.setLeft(left);
     result.setRight(right);
@@ -294,9 +244,9 @@ aspect GrammarExtension {
     }
     b.append("<");
     if (!getName().equals("")) {
-      b.append(internalName()).append(":");
+      b.append(toMustache().internalName()).append(":");
     }
-    getJavaTypeUse().generateAbstractGrammar(b);
+    effectiveJavaTypeUse().generateAbstractGrammar(b);
     b.append(">");
     if (getNTA()) {
       b.append("/");
diff --git a/src/main/jastadd/backend/Mappings.jrag b/src/main/jastadd/backend/Mappings.jrag
index 07cbf5b81a531638ac092635f915ed856a65df4c..7aa82d304079592d605997484d1348a3d3681875 100644
--- a/src/main/jastadd/backend/Mappings.jrag
+++ b/src/main/jastadd/backend/Mappings.jrag
@@ -1,117 +1,91 @@
 aspect DefaultMappings {
 
-  private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinitionFromBytes(String typeName) {
-    DefaultMappingDefinition result = new DefaultMappingDefinition();
-    result.setID("_DefaultBytesTo" + Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1) + "Mapping");
-    result.setFromType(new JavaArrayMappingDefinitionType(new SimpleJavaTypeUse("byte")));
-    result.setFromVariableName("bytes");
-    result.setToType(new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName)));
-    return result;
-  }
-
-  private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinitionToBytes(String typeName) {
-    DefaultMappingDefinition result = new DefaultMappingDefinition();
-    result.setID("_Default" + Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1) + "ToBytesMapping");
-    result.setFromType(new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName)));
-    result.setFromVariableName("input");
-    result.setToType(new JavaArrayMappingDefinitionType(new SimpleJavaTypeUse("byte")));
-    return result;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultBytesToIntMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("int");
-    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getInt();");
-    return result;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultBytesToShortMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("short");
-    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getShort();");
-    return result;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultBytesToLongMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("long");
-    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getLong();");
-    return result;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultBytesToFloatMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("float");
-    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getFloat();");
-    return result;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultBytesToDoubleMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("double");
-    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getDouble();");
-    return result;
+  private String RagConnect.baseDefaultMappingTypeNamePart(String typeName) {
+    return capitalize(typeName).replace("[]", "s");
   }
 
-  syn nta DefaultMappingDefinition RagConnect.defaultBytesToCharMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("char");
-    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getChar();");
-    return result;
+  private MappingDefinitionType RagConnect.baseDefaultMappingTypeFromName(String typeName) {
+    return typeName.endsWith("[]") ?
+        new JavaArrayMappingDefinitionType(new SimpleJavaTypeUse(typeName.replace("[]", ""))) :
+        new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName));
   }
 
-  syn nta DefaultMappingDefinition RagConnect.defaultBytesToStringMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("String");
-    result.setContent("return new String(bytes);");
-    return result;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultIntToBytesMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("int");
-    result.setContent("return java.nio.ByteBuffer.allocate(4).putInt(input).array();");
+  private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinition(String fromTypeName, String toTypeName, String content) {
+    DefaultMappingDefinition result = new DefaultMappingDefinition();
+    result.setID("_Default" + baseDefaultMappingTypeNamePart(fromTypeName) + "To" + baseDefaultMappingTypeNamePart(toTypeName) + "Mapping");
+    result.setFromType(baseDefaultMappingTypeFromName(fromTypeName));
+    result.setFromVariableName("input");
+    result.setToType(baseDefaultMappingTypeFromName(toTypeName));
+    result.setContent(content);
     return result;
   }
 
-  syn nta DefaultMappingDefinition RagConnect.defaultShortToBytesMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("short");
-    result.setContent("return java.nio.ByteBuffer.allocate(2).putShort(input).array();");
-    return result;
-  }
+  syn nta DefaultMappingDefinition RagConnect.defaultBytesToIntMapping() = baseDefaultMappingDefinition(
+      "byte[]", "int", "return java.nio.ByteBuffer.wrap(input).getInt();");
+  syn nta DefaultMappingDefinition RagConnect.defaultBytesToShortMapping() = baseDefaultMappingDefinition(
+      "byte[]", "short", "return java.nio.ByteBuffer.wrap(input).getShort();");
+  syn nta DefaultMappingDefinition RagConnect.defaultBytesToLongMapping() = baseDefaultMappingDefinition(
+      "byte[]", "long", "return java.nio.ByteBuffer.wrap(input).getLong();");
+  syn nta DefaultMappingDefinition RagConnect.defaultBytesToFloatMapping() = baseDefaultMappingDefinition(
+      "byte[]", "float", "return java.nio.ByteBuffer.wrap(input).getFloat();");
+  syn nta DefaultMappingDefinition RagConnect.defaultBytesToDoubleMapping() = baseDefaultMappingDefinition(
+      "byte[]", "double", "return java.nio.ByteBuffer.wrap(input).getDouble();");
+  syn nta DefaultMappingDefinition RagConnect.defaultBytesToCharMapping() = baseDefaultMappingDefinition(
+      "byte[]", "char", "return java.nio.ByteBuffer.wrap(input).getChar();");
+  syn nta DefaultMappingDefinition RagConnect.defaultBytesToStringMapping() = baseDefaultMappingDefinition(
+      "byte[]", "String", "return new String(input);");
 
-  syn nta DefaultMappingDefinition RagConnect.defaultLongToBytesMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("long");
-    result.setContent("return java.nio.ByteBuffer.allocate(8).putLong(input).array();");
-    return result;
-  }
+  syn nta DefaultMappingDefinition RagConnect.defaultIntToBytesMapping() = baseDefaultMappingDefinition(
+      "int", "byte[]", "return java.nio.ByteBuffer.allocate(Integer.BYTES).putInt(input).array();");
+  syn nta DefaultMappingDefinition RagConnect.defaultShortToBytesMapping() = baseDefaultMappingDefinition(
+      "short", "byte[]", "return java.nio.ByteBuffer.allocate(Short.BYTES).putShort(input).array();");
+  syn nta DefaultMappingDefinition RagConnect.defaultLongToBytesMapping() = baseDefaultMappingDefinition(
+      "long", "byte[]", "return java.nio.ByteBuffer.allocate(Long.BYTES).putLong(input).array();");
+  syn nta DefaultMappingDefinition RagConnect.defaultFloatToBytesMapping() = baseDefaultMappingDefinition(
+      "float", "byte[]", "return java.nio.ByteBuffer.allocate(Float.BYTES).putFloat(input).array();");
+  syn nta DefaultMappingDefinition RagConnect.defaultDoubleToBytesMapping() = baseDefaultMappingDefinition(
+      "double", "byte[]", "return java.nio.ByteBuffer.allocate(Double.BYTES).putDouble(input).array();");
+  syn nta DefaultMappingDefinition RagConnect.defaultCharToBytesMapping() = baseDefaultMappingDefinition(
+      "char", "byte[]", "return java.nio.ByteBuffer.allocate(Character.BYTES).putChar(input).array();");
+  syn nta DefaultMappingDefinition RagConnect.defaultStringToBytesMapping() = baseDefaultMappingDefinition(
+      "String", "byte[]", "return input.getBytes();");
 
-  syn nta DefaultMappingDefinition RagConnect.defaultFloatToBytesMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("float");
-    result.setContent("return java.nio.ByteBuffer.allocate(4).putFloat(input).array();");
-    return result;
-  }
+  syn nta DefaultMappingDefinition RagConnect.defaultStringToIntMapping() = baseDefaultMappingDefinition(
+      "String", "int", "return Integer.parseInt(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultStringToShortMapping() = baseDefaultMappingDefinition(
+      "String", "short", "return Short.parseShort(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultStringToLongMapping() = baseDefaultMappingDefinition(
+      "String", "long", "return Long.parseLong(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultStringToFloatMapping() = baseDefaultMappingDefinition(
+      "String", "float", "return Float.parseFloat(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultStringToDoubleMapping() = baseDefaultMappingDefinition(
+      "String", "double", "return Double.parseDouble(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultStringToCharMapping() = baseDefaultMappingDefinition(
+      "String", "char", "return input.charAt(0);");
 
-  syn nta DefaultMappingDefinition RagConnect.defaultDoubleToBytesMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("double");
-    result.setContent("return java.nio.ByteBuffer.allocate(8).putDouble(input).array();");
-    return result;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultCharToBytesMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("char");
-    result.setContent("return java.nio.ByteBuffer.allocate(2).putChar(input).array();");
-    return result;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultStringToBytesMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("String");
-    result.setContent("return input.getBytes();");
-    return result;
-  }
+  syn nta DefaultMappingDefinition RagConnect.defaultIntToStringMapping() = baseDefaultMappingDefinition(
+      "int", "String", "return String.valueOf(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultShortToStringMapping() = baseDefaultMappingDefinition(
+      "short", "String", "return String.valueOf(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultLongToStringMapping() = baseDefaultMappingDefinition(
+      "long", "String", "return String.valueOf(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultFloatToStringMapping() = baseDefaultMappingDefinition(
+      "float", "String", "return String.valueOf(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultDoubleToStringMapping() = baseDefaultMappingDefinition(
+      "double", "String", "return String.valueOf(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultCharToStringMapping() = baseDefaultMappingDefinition(
+      "char", "String", "return String.valueOf(input);");
 }
 
 aspect Mappings {
   // --- effectiveMappings ---
   syn java.util.List<MappingDefinition> EndpointDefinition.effectiveMappings();
-  eq ReceiveFromMqttDefinition.effectiveMappings() {
-    // if there is a first mapping, check if its input type is byte[].
+  eq ReceiveTokenEndpointDefinition.effectiveMappings() {
+    // if there is a first mapping, check if it is suitable.
     //  or if no mappings are specified.
     // then prepend the suitable default mapping
     java.util.List<MappingDefinition> result;
-    if (getMappingList().isEmpty() || !getMappingList().get(0).getFromType().isByteArray()) {
+    if (getMappingList().isEmpty() || !hasSuitableEdgeMapping()) {
       result = new java.util.ArrayList();
       result.add(suitableDefaultMapping());
       result.addAll(getMappingList());
@@ -120,15 +94,13 @@ aspect Mappings {
     }
     return result;
   }
-  eq SendToMqttDefinition.effectiveMappings() {
-    // if there is a mapping, check if the output type of the last mapping is byte[].
+  eq SendTokenEndpointDefinition.effectiveMappings() {
+    // if there is a mapping, check if it is suitable.
     //  or if no mappings are specified.
     // then append the suitable default mapping
     java.util.List<MappingDefinition> result;
-    int numMappings = getMappingList().size();
-    if (numMappings == 0 || !getMappingList().get(numMappings - 1).getToType().isByteArray()) {
-      result = new java.util.ArrayList();
-      result.addAll(getMappingList());
+    if (getMappingList().isEmpty() || !hasSuitableEdgeMapping()) {
+      result = new java.util.ArrayList(getMappingList());
       result.add(suitableDefaultMapping());
     } else {
       result = getMappingList();
@@ -136,8 +108,18 @@ aspect Mappings {
     return result;
   }
 
+  // --- hasSuitableEdgeMapping ---
+  syn boolean TokenEndpointDefinition.hasSuitableEdgeMapping();
+  eq ReceiveTokenEndpointDefinition.hasSuitableEdgeMapping() = isSuitableEdgeMapping(getMappingList().get(0));
+  eq SendTokenEndpointDefinition.hasSuitableEdgeMapping() = isSuitableEdgeMapping(getMappingList().get(getMappingList().size() - 1));
+
+  // --- isSuitableEdgeMapping(def) ---
+  syn boolean TokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def);
+  eq ReceiveTokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def) = def.getFromType().isByteArray();
+  eq SendTokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def) = def.getToType().isByteArray();
+
   // --- isPrimitiveType ---
-  syn boolean TokenComponent.isPrimitiveType() = getJavaTypeUse().isPrimitiveType();
+  syn boolean TokenComponent.isPrimitiveType() = effectiveJavaTypeUse().isPrimitiveType();
   syn boolean JavaTypeUse.isPrimitiveType() = false;
   eq SimpleJavaTypeUse.isPrimitiveType() {
     switch(getName()) {
@@ -160,9 +142,9 @@ aspect Mappings {
 
   // --- suitableDefaultMapping ---
   syn DefaultMappingDefinition EndpointDefinition.suitableDefaultMapping();
-  eq ReceiveFromMqttDefinition.suitableDefaultMapping() {
+  eq ReceiveTokenEndpointDefinition.suitableDefaultMapping() {
     String typeName = getMappingList().isEmpty() ?
-        getToken().getJavaTypeUse().getName() :
+        getToken().effectiveJavaTypeUse().getName() :
         getMappingList().get(0).getFromType().prettyPrint();
     switch(typeName) {
       case "int":
@@ -181,9 +163,9 @@ aspect Mappings {
       default: return null;
     }
   }
-  eq SendToMqttDefinition.suitableDefaultMapping() {
+  eq SendTokenEndpointDefinition.suitableDefaultMapping() {
     String typeName = getMappingList().isEmpty() ?
-        getToken().getJavaTypeUse().getName() :
+        getToken().effectiveJavaTypeUse().getName() :
         getMappingList().get(getMappingList().size() - 1).getFromType().prettyPrint();
     switch(typeName) {
       case "int":
@@ -202,15 +184,60 @@ aspect Mappings {
       default: return null;
     }
   }
+//  eq ReceiveFromRestDefinition.suitableDefaultMapping() {
+//    String typeName = getMappingList().isEmpty() ?
+//        getToken().getJavaTypeUse().getName() :
+//        getMappingList().get(0).getFromType().prettyPrint();
+//    switch(typeName) {
+//      case "int":
+//      case "Integer": return ragconnect().defaultStringToIntMapping();
+//      case "short":
+//      case "Short": return ragconnect().defaultStringToShortMapping();
+//      case "long":
+//      case "Long": return ragconnect().defaultStringToLongMapping();
+//      case "float":
+//      case "Float": return ragconnect().defaultStringToFloatMapping();
+//      case "double":
+//      case "Double": return ragconnect().defaultStringToDoubleMapping();
+//      case "char":
+//      case "Character": return ragconnect().defaultStringToCharMapping();
+//      default: return null;
+//    }
+//  }
+//  eq SendToRestDefinition.suitableDefaultMapping() {
+//    String typeName = getMappingList().isEmpty() ?
+//        getToken().getJavaTypeUse().getName() :
+//        getMappingList().get(getMappingList().size() - 1).getFromType().prettyPrint();
+//    switch(typeName) {
+//      case "int":
+//      case "Integer": return ragconnect().defaultIntToStringMapping();
+//      case "short":
+//      case "Short": return ragconnect().defaultShortToStringMapping();
+//      case "long":
+//      case "Long": return ragconnect().defaultLongToStringMapping();
+//      case "float":
+//      case "Float": return ragconnect().defaultFloatToStringMapping();
+//      case "double":
+//      case "Double": return ragconnect().defaultDoubleToStringMapping();
+//      case "char":
+//      case "Character": return ragconnect().defaultCharToStringMapping();
+//      default: return null;
+//    }
+//  }
 
   // --- isByteArray ---
   syn boolean MappingDefinitionType.isByteArray() = false;
   eq JavaArrayMappingDefinitionType.isByteArray() = getType().getName().equals("byte");
 
+//  // --- isString ---
+//  syn boolean MappingDefinitionType.isString() = false;
+//  eq JavaMappingDefinitionType.isString() = getType().getName().equals("String");
+
   // --- allMappingDefinitions ---
   syn java.util.List<MappingDefinition> RagConnect.allMappingDefinitions() {
     java.util.List<MappingDefinition> result = new java.util.ArrayList<>();
     getMappingDefinitionList().iterator().forEachRemaining(result::add);
+    // byte[] conversion
     result.add(defaultBytesToIntMapping());
     result.add(defaultBytesToShortMapping());
     result.add(defaultBytesToLongMapping());
@@ -225,6 +252,19 @@ aspect Mappings {
     result.add(defaultDoubleToBytesMapping());
     result.add(defaultCharToBytesMapping());
     result.add(defaultStringToBytesMapping());
+//    // string conversion
+//    result.add(defaultStringToIntMapping());
+//    result.add(defaultStringToShortMapping());
+//    result.add(defaultStringToLongMapping());
+//    result.add(defaultStringToFloatMapping());
+//    result.add(defaultStringToDoubleMapping());
+//    result.add(defaultStringToCharMapping());
+//    result.add(defaultIntToStringMapping());
+//    result.add(defaultShortToStringMapping());
+//    result.add(defaultLongToStringMapping());
+//    result.add(defaultFloatToStringMapping());
+//    result.add(defaultDoubleToStringMapping());
+//    result.add(defaultCharToStringMapping());
     return result;
   }
 }
diff --git a/src/main/jastadd/backend/MustacheNodesToYAML.jrag b/src/main/jastadd/backend/MustacheNodesToYAML.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..2894d3a8a531a190e8146121dc8e7c97f2cecc91
--- /dev/null
+++ b/src/main/jastadd/backend/MustacheNodesToYAML.jrag
@@ -0,0 +1,155 @@
+aspect MustacheNodesToYAML {
+  syn Document MRagConnect.toYAML() {
+    Document doc = new Document();
+    MappingElement root = new MappingElement();
+    root.addKeyValuePair("rootNodeName", StringElement.of(rootNodeName()));
+    root.addKeyValuePair("closeMethod", StringElement.of(closeMethod()));
+    root.addKeyValuePair("usesMqtt", ValueElement.of(usesMqtt));
+    root.addKeyValuePair("usesRest", ValueElement.of(usesRest));
+    // mqtt
+    root.addKeyValuePair("mqttHandlerField", StringElement.of(mqttHandlerField()));
+    root.addKeyValuePair("mqttHandlerAttribute", StringElement.of(mqttHandlerAttribute()));
+    root.addKeyValuePair("mqttSetupWaitUntilReadyMethod", StringElement.of(mqttSetupWaitUntilReadyMethod()));
+
+    // rootTypeComponents
+    ListElement rootTypeComponents = new ListElement();
+    for (MTypeComponent comp : getRootTypeComponentList()) {
+      MappingElement inner = new MappingElement();
+      inner.addKeyValuePair("first", ValueElement.of(comp.isFirst()));
+      inner.addKeyValuePair("name", StringElement.of(comp.name()));
+      rootTypeComponents.addElement(inner);
+    }
+    root.addKeyValuePair("RootTypeComponents", rootTypeComponents);
+
+    // rest
+    root.addKeyValuePair("restHandlerField", StringElement.of(restHandlerField()));
+    root.addKeyValuePair("restHandlerAttribute", StringElement.of(restHandlerAttribute()));
+
+    // ReceiveDefinitions
+    ListElement receiveDefinitions = new ListElement();
+    for (MReceiveDefinition def : getReceiveDefinitionList()) {
+      receiveDefinitions.addElement(def.toYAML());
+    }
+    root.addKeyValuePair("ReceiveDefinitions", receiveDefinitions);
+
+    // SendDefinitions
+    ListElement sendDefinitions = new ListElement();
+    for (MSendDefinition def : getSendDefinitionList()) {
+      sendDefinitions.addElement(def.toYAML());
+    }
+    root.addKeyValuePair("SendDefinitions", sendDefinitions);
+
+    // MappingDefinitions
+    ListElement mappingDefinitions = new ListElement();
+    for (MMappingDefinition def : getMappingDefinitionList()) {
+      mappingDefinitions.addElement(def.toYAML());
+    }
+    root.addKeyValuePair("MappingDefinitions", mappingDefinitions);
+
+    // DependencyDefinitions
+    ListElement dependencyDefinitions = new ListElement();
+    for (MDependencyDefinition def : getDependencyDefinitionList()) {
+      dependencyDefinitions.addElement(def.toYAML());
+    }
+    root.addKeyValuePair("DependencyDefinitions", dependencyDefinitions);
+
+    // TokenComponents
+    ListElement tokenComponents = new ListElement();
+    for (MTokenComponent comp : getTokenComponentList()) {
+      tokenComponents.addElement(comp.toYAML());
+    }
+    root.addKeyValuePair("TokenComponents", tokenComponents);
+
+    doc.addComplexElement(root);
+    return doc;
+  }
+
+  syn MappingElement MEndpointDefinition.toYAML() {
+    MappingElement result = new MappingElement();
+    result.addKeyValuePair("parentTypeName", StringElement.of(parentTypeName()));
+    result.addKeyValuePair("connectMethod", StringElement.of(connectMethod()));
+    result.addKeyValuePair("connectParameterName", StringElement.of(connectParameterName()));
+    result.addKeyValuePair("lastDefinitionToType", StringElement.of(lastDefinitionToType()));
+    result.addKeyValuePair("preemptiveReturn", StringElement.of(preemptiveReturn()));
+    result.addKeyValuePair("alwaysApply", ValueElement.of(alwaysApply()));
+    result.addKeyValuePair("condition", StringElement.of(
+    condition().replace("\"", "\\\"").replace("\n", "\\n")));
+    result.addKeyValuePair("lastResult", StringElement.of(lastResult()));
+    result.addKeyValuePair("tokenName", StringElement.of(tokenName()));
+    result.addKeyValuePair("InnerMappingDefinitions", innerMappingDefinitionsAsListElement());
+    return result;
+  }
+
+  syn MappingElement MReceiveDefinition.toYAML() {
+    MappingElement result = super.toYAML();
+    result.addKeyValuePair("loggingEnabledForReads", ValueElement.of(loggingEnabledForReads));
+    return result;
+  }
+
+  syn MappingElement MSendDefinition.toYAML() {
+    MappingElement result = super.toYAML();
+    result.addKeyValuePair("sender", StringElement.of(sender()));
+    result.addKeyValuePair("lastValue", StringElement.of(lastValue()));
+    result.addKeyValuePair("loggingEnabledForWrites", ValueElement.of(loggingEnabledForWrites));
+    result.addKeyValuePair("updateMethod", StringElement.of(updateMethod()));
+    result.addKeyValuePair("writeMethod", StringElement.of(writeMethod()));
+    result.addKeyValuePair("tokenResetMethod", StringElement.of(tokenResetMethod()));
+    return result;
+  }
+
+  syn Element MMappingDefinition.toYAML() {
+    MappingElement result = new MappingElement();
+    result.addKeyValuePair("toType", StringElement.of(toType()));
+    result.addKeyValuePair("methodName", StringElement.of(methodName()));
+    result.addKeyValuePair("fromType", StringElement.of(fromType()));
+    result.addKeyValuePair("fromVariableName", StringElement.of(fromVariableName()));
+    result.addKeyValuePair("content", StringElement.of(
+        content().replace("\"", "\\\"").replace("\n", "\\n")));
+    return result;
+  }
+
+  syn Element MDependencyDefinition.toYAML() {
+    MappingElement result = new MappingElement();
+    result.addKeyValuePair("targetParentTypeName", StringElement.of(targetParentTypeName()));
+    result.addKeyValuePair("dependencyMethod", StringElement.of(dependencyMethod()));
+    result.addKeyValuePair("sourceParentTypeName", StringElement.of(sourceParentTypeName()));
+    result.addKeyValuePair("internalRelationPrefix", StringElement.of(internalRelationPrefix()));
+    return result;
+  }
+
+  syn Element MTokenComponent.toYAML() {
+    MappingElement result = new MappingElement();
+    result.addKeyValuePair("parentTypeName", StringElement.of(parentTypeName()));
+    result.addKeyValuePair("name", StringElement.of(name()));
+    result.addKeyValuePair("javaType", StringElement.of(javaType()));
+    result.addKeyValuePair("internalName", StringElement.of(internalName()));
+    ListElement dependencyDefinitions = new ListElement();
+    for (MDependencyDefinition def : getDependencyDefinitionList()) {
+      MappingElement inner = new MappingElement();
+      inner.addKeyValuePair("targetParentTypeName", StringElement.of(def.targetParentTypeName()));
+      inner.addKeyValuePair("internalRelationPrefix", StringElement.of(def.internalRelationPrefix()));
+      MappingElement targetEndpointDefinition = new MappingElement();
+    targetEndpointDefinition.addKeyValuePair("updateMethod", StringElement.of(def.targetEndpointDefinition().updateMethod()));
+    targetEndpointDefinition.addKeyValuePair("writeMethod", StringElement.of(def.targetEndpointDefinition().writeMethod()));
+      inner.addKeyValuePair("targetEndpointDefinition", targetEndpointDefinition);
+      dependencyDefinitions.addElement(inner);
+    }
+    result.addKeyValuePair("DependencyDefinitions", dependencyDefinitions);
+    return result;
+  }
+
+  ListElement MEndpointDefinition.innerMappingDefinitionsAsListElement() {
+    ListElement innerMappingDefinitions = new ListElement();
+    for (MInnerMappingDefinition def : getInnerMappingDefinitionList()) {
+      MappingElement inner = new MappingElement();
+      inner.addKeyValuePair("toType", StringElement.of(def.toType()));
+      inner.addKeyValuePair("methodName", StringElement.of(def.methodName()));
+      inner.addKeyValuePair("inputVarName", StringElement.of(def.inputVarName()));
+      inner.addKeyValuePair("outputVarName", StringElement.of(def.outputVarName()));
+      inner.addKeyValuePair("last", ValueElement.of(def.isLast()));
+      innerMappingDefinitions.addElement(inner);
+    }
+    return innerMappingDefinitions;
+  }
+
+}
diff --git a/src/main/jastadd/parser/RagConnect.parser b/src/main/jastadd/parser/RagConnect.parser
index e2685c15555056c64d4f5e8bebdc458ecea981a3..0432b0df96c44060f6df1d6918a856f62810230d 100644
--- a/src/main/jastadd/parser/RagConnect.parser
+++ b/src/main/jastadd/parser/RagConnect.parser
@@ -10,41 +10,35 @@ RagConnect ragconnect
   private Iterable<String> makeMappingDefs(ArrayList<?> raw_mapping_defs) {
     return () -> raw_mapping_defs.stream().map(raw -> ((Symbol) raw).value.toString()).iterator();
   }
+  private TokenEndpointDefinition enableAlwaysApply(TokenEndpointDefinition def) {
+    def.setAlwaysApply(true);
+    return def;
+  }
 :} ;
 
 EndpointDefinition endpoint_definition
-  = RECEIVE ID.type_name DOT ID.token_name SCOL
+  = endpoint_definition_type.endpointDef SCOL
     {:
-      ReceiveFromMqttDefinition result = new ReceiveFromMqttDefinition();
-      result.setToken(TokenComponent.createRef(type_name + "." + token_name));
-      return result;
+      return endpointDef;
     :}
-  | RECEIVE ID.type_name DOT ID.token_name USING string_list.mapping_defs SCOL
+  | endpoint_definition_type.endpointDef USING string_list.mapping_defs SCOL
     {:
-      ReceiveFromMqttDefinition result = new ReceiveFromMqttDefinition();
-      result.setToken(TokenComponent.createRef(type_name + "." + token_name));
       for (String mapping_def : makeMappingDefs(mapping_defs)) {
-        result.addMapping(MappingDefinition.createRef(mapping_def));
+        endpointDef.addMapping(MappingDefinition.createRef(mapping_def));
       }
-      return result;
-    :}
-  | SEND ID.type_name DOT ID.token_name SCOL
-    {:
-      SendToMqttDefinition result = new SendToMqttDefinition();
-      result.setToken(TokenComponent.createRef(type_name + "." + token_name));
-      return result;
-    :}
-  | SEND ID.type_name DOT ID.token_name USING string_list.mapping_defs SCOL
-    {:
-      SendToMqttDefinition result = new SendToMqttDefinition();
-      result.setToken(TokenComponent.createRef(type_name + "." + token_name));
-      for (String mapping_def : makeMappingDefs(mapping_defs)) {
-        result.addMapping(MappingDefinition.createRef(mapping_def));
-      }
-      return result;
+      return endpointDef;
     :}
 ;
 
+EndpointDefinition endpoint_definition_type
+  = RECEIVE token_ref           {: return new ReceiveTokenEndpointDefinition().setToken(token_ref); :}
+  | SEND token_ref              {: return new SendTokenEndpointDefinition().setToken(token_ref); :}
+;
+
+TokenComponent token_ref
+  = ID.type_name DOT ID.token_name  {: return TokenComponent.createRef(type_name + "." + token_name); :}
+;
+
 ArrayList string_list
   = ID
   | string_list COMMA ID
diff --git a/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java b/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java
index 8b33412c7994b5443088de6249273b9b83374414..711169ec2ea7d9c21b48dc52c5a7d2cf4cc8b427 100644
--- a/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java
+++ b/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java
@@ -14,18 +14,21 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.StandardCopyOption;
-import java.util.Collection;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
+import java.util.*;
 
 public class Compiler extends AbstractCompiler {
 
 //  private ValueOption optionOutputDir;
   private ValueOption optionRootNode;
+  private ValueOption optionProtocols;
+  private BooleanOption optionPrintYaml;
   private BooleanOption optionVerbose;
   private BooleanOption optionLogReads;
   private BooleanOption optionLogWrites;
 
+  private static final String OPTION_PROTOCOL_MQTT = "mqtt";
+  private static final String OPTION_PROTOCOL_REST = "rest";
+
   public Compiler() {
     super("ragconnect", true);
   }
@@ -51,6 +54,10 @@ public class Compiler extends AbstractCompiler {
       }
     }
 
+    if (!optionRootNode.isMatched()) {
+      return error("Root node not specified");
+    }
+
     RagConnect ragConnect = parseProgram(getConfiguration().getFiles());
 
     if (!ragConnect.errors().isEmpty()) {
@@ -61,25 +68,41 @@ public class Compiler extends AbstractCompiler {
       System.exit(1);
     }
 
+    if (optionPrintYaml.value()) {
+      ASTNode.rootNode = ragConnect.getProgram().resolveTypeDecl(optionRootNode.value());
+      String yamlContent = ragConnect.toMustache().toYAML().prettyPrint();
+      System.out.println(yamlContent);
+      writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.yml"), yamlContent);
+      return 0;
+    }
+
     printMessage("Writing output files");
-    // copy MqttHandler into outputDir
-    final String mqttHandlerFileName = "MqttHandler.jadd";
-    try {
-      InputStream inputStream = Compiler.class.getClassLoader().getResourceAsStream(mqttHandlerFileName);
-      if (inputStream == null) {
-        throw new CompilerException("Could not open " + mqttHandlerFileName);
+    final List<String> handlers = new ArrayList<>();
+    if (ASTNode.usesMqtt) {
+      handlers.add("MqttHandler.jadd");
+    }
+    if (ASTNode.usesRest) {
+      handlers.add("RestHandler.jadd");
+    }
+    // copy handlers into outputDir
+    for (String handlerFileName : handlers) {
+      try {
+        InputStream inputStream = Compiler.class.getClassLoader().getResourceAsStream(handlerFileName);
+        if (inputStream == null) {
+          throw new CompilerException("Could not open " + handlerFileName);
+        }
+        Files.copy(inputStream,
+            getConfiguration().outputDir().toPath().resolve(handlerFileName),
+            StandardCopyOption.REPLACE_EXISTING);
+      } catch (IOException e) {
+        throw new CompilerException("Could not copy " + handlerFileName, e);
       }
-      Files.copy(inputStream,
-          getConfiguration().outputDir().toPath().resolve(mqttHandlerFileName),
-          StandardCopyOption.REPLACE_EXISTING);
-    } catch (IOException e) {
-      throw new CompilerException("Could not copy " + mqttHandlerFileName, e);
     }
     for (GrammarFile grammarFile : ragConnect.getProgram().getGrammarFileList()) {
       Path outputFile = getConfiguration().outputDir().toPath().resolve(grammarFile.getFileName());
-      sendToFile(outputFile, grammarFile.generateAbstractGrammar());
+      writeToFile(outputFile, grammarFile.generateAbstractGrammar());
     }
-    sendToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.jadd"), ragConnect.generateAspect(optionRootNode.value()));
+    writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.jadd"), ragConnect.generateAspect(optionRootNode.value()));
     return 0;
   }
 
@@ -89,8 +112,8 @@ public class Compiler extends AbstractCompiler {
     try {
       new Compiler().run(args);
     } catch (CompilerException e) {
-      System.err.println(e.getMessage());
-      System.exit(-1);
+      e.printStackTrace();
+      System.exit(1);
     }
   }
 
@@ -118,7 +141,7 @@ public class Compiler extends AbstractCompiler {
     System.out.println(message);
   }
 
-  private void sendToFile(Path path, String str) throws CompilerException {
+  private void writeToFile(Path path, String str) throws CompilerException {
     try (BufferedWriter writer = Files.newBufferedWriter(path)) {
       writer.append(str);
     } catch (Exception e) {
@@ -132,6 +155,15 @@ public class Compiler extends AbstractCompiler {
         new ValueOption("rootNode", "root node in the base grammar.")
         .acceptAnyValue()
         .needsValue(true));
+    optionProtocols = addOption(
+        new ValueOption("protocols", "Protocols to enable")
+        .acceptMultipleValues(true)
+        .addDefaultValue(OPTION_PROTOCOL_MQTT, "Enable MQTT")
+        .addAcceptedValue(OPTION_PROTOCOL_REST, "Enable REST")
+    );
+    optionPrintYaml = addOption(
+        new BooleanOption("printYaml", "Print out YAML instead of generating files")
+        .defaultValue(false));
     optionVerbose = addOption(
         new BooleanOption("verbose", "Print more messages while compiling.")
         .defaultValue(false));
@@ -145,6 +177,8 @@ public class Compiler extends AbstractCompiler {
 
   private RagConnect parseProgram(Collection<String> files) throws CompilerException {
     Program program = new Program();
+    boolean atLeastOneGrammar = false;
+    boolean atLeastOneRagConnect = false;
 
     RagConnect ragConnect = new RagConnect();
     ragConnect.setProgram(program);
@@ -160,20 +194,34 @@ public class Compiler extends AbstractCompiler {
         case "relast":
           // processGrammar
           parseGrammar(program, filename);
+          atLeastOneGrammar = true;
           break;
         case "connect":
+        case "ragconnect":
           // process ragConnect
           RagConnect parsedRagConnect = parseRagConnect(program, filename);
           mergeRagConnectDefinitions(ragConnect, parsedRagConnect);
+          atLeastOneRagConnect = true;
           break;
         default:
           throw new CompilerException("Unknown file extension in " + filename);
       }
     }
+    if (!atLeastOneGrammar) {
+      System.err.println("No grammar file specified! (*.ast, *.relast)");
+    }
+    if (!atLeastOneRagConnect) {
+      System.err.println("No ragconnect file specified! (*.connect, *.ragconnect)");
+    }
+    if (!atLeastOneGrammar && !atLeastOneRagConnect) {
+      System.exit(1);
+    }
     ragConnect.treeResolveAll();
     ragConnect.additionalRelations().forEach(ragConnectGrammarPart::addDeclaration);
     ASTNode.loggingEnabledForReads = optionLogReads.value();
     ASTNode.loggingEnabledForWrites = optionLogWrites.value();
+    ASTNode.usesMqtt = optionProtocols.hasValue(OPTION_PROTOCOL_MQTT);
+    ASTNode.usesRest = optionProtocols.hasValue(OPTION_PROTOCOL_REST);
     return ragConnect;
   }
 
diff --git a/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java b/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java
index ac1109fef423ed58ebbbdfcaeacbdca42745e341..ee9ed046348efeaa4b12dbc816c55414d93289cf 100644
--- a/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java
+++ b/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java
@@ -1,15 +1,11 @@
 package org.jastadd.ragconnect.compiler;
 
-import beaver.Parser;
 import org.jastadd.ragconnect.ast.*;
 import org.jastadd.ragconnect.parser.RagConnectParser;
 import org.jastadd.ragconnect.scanner.RagConnectScanner;
 
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.nio.ByteBuffer;
+import java.io.Reader;
 import java.nio.file.Files;
-import java.nio.file.Path;
 import java.nio.file.Paths;
 
 /**
@@ -19,141 +15,87 @@ import java.nio.file.Paths;
  */
 public class SimpleMain {
 
-  // --- just testing byte[] conversion ---
-  public static void testing() {
-    byte[] bytes;
-    int i = 1;
-    double d = 2.3d;
-    float f = 4.2f;
-    short sh = 13;
-    long l = 7L;
-    String s = "Hello";
-    char c = 'a';
-
-    Integer ii = Integer.valueOf(1);
-    if (!ii.equals(i)) throw new AssertionError("Ints not equal");
-
-    // int to byte
-    ByteBuffer i2b = ByteBuffer.allocate(4);
-    i2b.putInt(i);
-    bytes = i2b.array();
-
-    // byte to int
-    ByteBuffer b2i = ByteBuffer.wrap(bytes);
-    int actual_i = b2i.getInt();
-    if (i != actual_i) throw new AssertionError("Ints not equal");
-
-    // double to byte
-    ByteBuffer d2b = ByteBuffer.allocate(8);
-    d2b.putDouble(d);
-    bytes = d2b.array();
-
-    // byte to double
-    ByteBuffer b2d = ByteBuffer.wrap(bytes);
-    double actual_d = b2d.getDouble();
-    if (d != actual_d) throw new AssertionError("Doubles not equal");
-
-    // float to byte
-    ByteBuffer f2b = ByteBuffer.allocate(4);
-    f2b.putFloat(f);
-    bytes = f2b.array();
-
-    // byte to float
-    ByteBuffer b2f = ByteBuffer.wrap(bytes);
-    float actual_f = b2f.getFloat();
-    if (f != actual_f) throw new AssertionError("Floats not equal");
-
-    // short to byte
-    ByteBuffer sh2b = ByteBuffer.allocate(2);
-    sh2b.putShort(sh);
-    bytes = sh2b.array();
-
-    // byte to short
-    ByteBuffer b2sh = ByteBuffer.wrap(bytes);
-    short actual_sh = b2sh.getShort();
-    if (sh != actual_sh) throw new AssertionError("Shorts not equal");
-
-    // long to byte
-    ByteBuffer l2b = ByteBuffer.allocate(8);
-    l2b.putLong(l);
-    bytes = l2b.array();
-
-    // byte to long
-    ByteBuffer b2l = ByteBuffer.wrap(bytes);
-    long actual_l = b2l.getLong();
-    if (l != actual_l) throw new AssertionError("Longs not equal");
-
-    // String to byte
-    bytes = s.getBytes();
-
-    // byte to String
-    String actual_s = new String(bytes);
-    if (!s.equals(actual_s)) throw new AssertionError("Strings not equal");
-
-    // char to byte
-    ByteBuffer c2b = ByteBuffer.allocate(2);
-    c2b.putChar(c);
-    bytes = c2b.array();
-
-    // byte to char
-    ByteBuffer b2c = ByteBuffer.wrap(bytes);
-    char actual_c = b2c.getChar();
-    if (c != actual_c) throw new AssertionError("Floats not equal");
-  }
-
-  public static void main(String[] args) {
-//    testing();
-    createManualAST();
-  }
-
-  private static void createManualAST() {
-    RagConnect model = new RagConnect();
-    Program program = parseProgram(Paths.get("ros2rag.starter","src", "main", "jastadd", "RobotModel.relast"));
-    model.setProgram(program);
-
-    MappingDefinition mappingDefinition = new MappingDefinition();
-    mappingDefinition.setID("PoseToPosition");
-    mappingDefinition.setFromType(makeMappingDefinitionType("int"));
-    mappingDefinition.setFromVariableName("x");
-    mappingDefinition.setToType(makeMappingDefinitionType("Position"));
-    mappingDefinition.setContent("      pose.position.x += sqrt(.5 * size.x)\n" +
-        "      MAP round(2)\n" +
-        "      x = x / 100\n" +
-        "      IGNORE_IF_SAME\n" +
-        "    ;");
-    model.addMappingDefinition(mappingDefinition);
-
-    ReceiveFromMqttDefinition receiveFromMqttDefinition = new ReceiveFromMqttDefinition();
-    receiveFromMqttDefinition.setAlwaysApply(false);
-    receiveFromMqttDefinition.setToken(TokenComponent.createRef("Link.CurrentPosition"));
-    receiveFromMqttDefinition.addMapping(mappingDefinition);
-    model.addEndpointDefinition(receiveFromMqttDefinition);
-
-    model.treeResolveAll();
-    for (ErrorMessage error : model.errors()) {
-      System.err.println(error);
-    }
-
-    System.out.println(model.generateAspect("Model"));
+  private static void printManualYAML() {
+    Document doc = new Document();
+    KeyValuePair root = new KeyValuePair();
+    root.setKey("panda_mqtt_connector");
+    MappingElement firstLevel = new MappingElement();
+    firstLevel.addKeyValuePair(new KeyValuePair("server", new StringElement("tcp://localhost:1883")));
+    firstLevel.addKeyValuePair(new KeyValuePair("robot_speed_factor", new ValueElement(".7")));
+
+    KeyValuePair topics = new KeyValuePair();
+    topics.setKey("topics");
+    MappingElement theTopics = new MappingElement();
+    theTopics.addKeyValuePair(new KeyValuePair("robotConfig", new StringElement("robotconfig")));
+    theTopics.addKeyValuePair(new KeyValuePair("trajectory", new StringElement("trajectory")));
+    theTopics.addKeyValuePair(new KeyValuePair("nextStep", new StringElement("ros2rag/nextStep")));
+    topics.setValue(theTopics);
+    firstLevel.addKeyValuePair(topics);
+
+    firstLevel.addKeyValuePair(new KeyValuePair("zone_size", new ValueElement("0.5")));
+
+    KeyValuePair zones = new KeyValuePair();
+    zones.setKey("zones");
+    ListElement theZones = new ListElement();
+    theZones.addElement(new StringElement("1 1"));
+    theZones.addElement(new StringElement("0 1"));
+    theZones.addElement(new StringElement("-1 1"));
+    zones.setValue(theZones);
+    firstLevel.addKeyValuePair(zones);
+
+    KeyValuePair parts = new KeyValuePair();
+    parts.setKey("end_effectors");
+    KeyValuePair pandaParts = new KeyValuePair();
+    pandaParts.setKey("panda");
+    MappingElement thePanda = new MappingElement();
+    thePanda.addKeyValuePair(new KeyValuePair("Link0", new StringElement("panda_link0")));
+    thePanda.addKeyValuePair(new KeyValuePair("Link1", new StringElement("panda_link1")));
+    thePanda.addKeyValuePair(new KeyValuePair("Link2", new StringElement("panda_link2")));
+    thePanda.addKeyValuePair(new KeyValuePair("Link3", new StringElement("panda_link3")));
+    thePanda.addKeyValuePair(new KeyValuePair("Link4", new StringElement("panda_link4")));
+    thePanda.addKeyValuePair(new KeyValuePair("Link5", new StringElement("panda_link5")));
+    thePanda.addKeyValuePair(new KeyValuePair("Link6", new StringElement("panda_link6")));
+    thePanda.addKeyValuePair(new KeyValuePair("RightFinger", new StringElement("panda_rightfinger")));
+    thePanda.addKeyValuePair(new KeyValuePair("LeftFinger", new StringElement("panda_leftfinger")));
+    pandaParts.setValue(thePanda);
+    parts.setValue(pandaParts);
+    firstLevel.addKeyValuePair(parts);
+
+    KeyValuePair end_effectors = new KeyValuePair();
+    end_effectors.setKey("end_effectors");
+    KeyValuePair endEffectorParts = new KeyValuePair();
+    endEffectorParts.setKey("panda");
+    endEffectorParts.setValue(new KeyValuePair("EndEffector", new StringElement("panda_hand")));
+    end_effectors.setValue(endEffectorParts);
+    firstLevel.addKeyValuePair(end_effectors);
+
+    KeyValuePair goalPoses = new KeyValuePair();
+    goalPoses.setKey("goal_poses");
+    ListElement theGoalPoses = new ListElement();
+    addPose(theGoalPoses, "0.4 0.4 0.3");
+    addPose(theGoalPoses, "-0.4 0.4 0.3");
+    addPose(theGoalPoses, "-0.4 -0.4 0.3");
+    addPose(theGoalPoses, "0.4 0.4 0.3");
+    addPose(theGoalPoses, "-0.4 0.4 0.3");
+    addPose(theGoalPoses, "0.4 0.4 0.3");
+    goalPoses.setValue(theGoalPoses);
+    firstLevel.addKeyValuePair(goalPoses);
+
+    root.setValue(firstLevel);
+    doc.addComplexElement(root);
+
+    System.out.println(doc.prettyPrint());
   }
 
-  private static MappingDefinitionType makeMappingDefinitionType(String type) {
-    JavaMappingDefinitionType result = new JavaMappingDefinitionType();
-    result.setType(new SimpleJavaTypeUse(type));
-    return result;
+  private static void addPose(ListElement theGoalPoses, String position) {
+    MappingElement goalPose1 = new MappingElement();
+    goalPose1.addKeyValuePair(new KeyValuePair("position", new StringElement(position)));
+    goalPose1.addKeyValuePair(new KeyValuePair("orientation", new StringElement("1 1 0 0")));
+    goalPose1.addKeyValuePair(new KeyValuePair("work", new StringElement("20000")));
+    theGoalPoses.addElement(goalPose1);
   }
 
-  private static Program parseProgram(Path path) {
-    try (BufferedReader reader = Files.newBufferedReader(path)) {
-      RagConnectScanner scanner = new RagConnectScanner(reader);
-      RagConnectParser parser = new RagConnectParser();
-      GrammarFile grammarFile = (GrammarFile) parser.parse(scanner);
-      Program program = new Program();
-      program.addGrammarFile(grammarFile);
-      return program;
-    } catch (IOException | Parser.Exception e) {
-      e.printStackTrace();
-    }
-    return null;
+  public static void main(String[] args) {
+    printManualYAML();
   }
 }
diff --git a/src/main/resources/MqttHandler.jadd b/src/main/resources/MqttHandler.jadd
index 493d8e4452e81c5dbfa45a522a6366345908cdcc..6f40612520cfb97151600dff53c8e0d384ab441e 100644
--- a/src/main/resources/MqttHandler.jadd
+++ b/src/main/resources/MqttHandler.jadd
@@ -1,4 +1,73 @@
-aspect MqttHandler {
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;aspect MqttHandler {
+public class MqttServerHandler {
+  private final java.util.Map<String, MqttHandler> handlers = new java.util.HashMap<>();
+  private long time;
+  private java.util.concurrent.TimeUnit unit;
+  private String name;
+
+  public MqttServerHandler() {
+    this("RagConnect");
+  }
+
+  public MqttServerHandler(String name) {
+    this.name = name;
+    setupWaitUntilReady(1, TimeUnit.SECONDS);
+  }
+
+  public void setupWaitUntilReady(long time, java.util.concurrent.TimeUnit unit) {
+    this.time = time;
+    this.unit = unit;
+  }
+
+  public MqttHandler resolveHandler(java.net.URI uri) throws IOException {
+    MqttHandler handler = handlers.get(uri.getHost());
+    if (handler == null) {
+      // first connect to that server
+      handler = new MqttHandler();
+      if (uri.getPort() == -1) {
+        handler.setHost(uri.getHost());
+      } else {
+        handler.setHost(uri.getHost(), uri.getPort());
+      }
+      handlers.put(uri.getHost(), handler);
+    }
+    handler.waitUntilReady(this.time, this.unit);
+    return handler;
+  }
+
+  public boolean newConnection(java.net.URI uri, java.util.function.Consumer<byte[]> callback) throws IOException {
+    return resolveHandler(uri).newConnection(extractTopic(uri), callback);
+  }
+
+  public void publish(java.net.URI uri, byte[] bytes) throws IOException {
+    resolveHandler(uri).publish(extractTopic(uri), bytes);
+  }
+
+  public void publish(java.net.URI uri, byte[] bytes, boolean retain) throws IOException {
+    resolveHandler(uri).publish(extractTopic(uri), bytes, retain);
+  }
+
+  public void publish(java.net.URI uri, byte[] bytes,
+                      org.fusesource.mqtt.client.QoS qos, boolean retain) throws IOException {
+    resolveHandler(uri).publish(extractTopic(uri), bytes, qos, retain);
+  }
+
+  private String extractTopic(java.net.URI uri) {
+    String path = uri.getPath();
+    if (path.charAt(0) == '/') {
+      path = path.substring(1);
+    }
+    return path;
+  }
+
+  public void close() {
+    for (MqttHandler handler : handlers.values()) {
+      handler.close();
+    }
+  }
+
+}
 /**
  * Helper class to receive updates via MQTT and use callbacks to handle those messages.
  *
@@ -14,10 +83,8 @@ public class MqttHandler {
   private java.net.URI host;
   /** The connection to the MQTT broker. */
   private org.fusesource.mqtt.client.CallbackConnection connection;
-  /** Whether we are subscribed to the topics yet */
-  private final java.util.concurrent.locks.Condition readyCondition;
-  private final java.util.concurrent.locks.Lock readyLock;
-  private boolean ready;
+  /** Whether we are connected yet */
+  private final java.util.concurrent.CountDownLatch readyLatch;
   private boolean sendWelcomeMessage = true;
   private org.fusesource.mqtt.client.QoS qos;
   /** Dispatch knowledge */
@@ -31,9 +98,7 @@ public class MqttHandler {
     this.name = java.util.Objects.requireNonNull(name, "Name must be set");
     this.logger = org.apache.logging.log4j.LogManager.getLogger(MqttHandler.class);
     this.callbacks = new java.util.HashMap<>();
-    this.readyLock = new java.util.concurrent.locks.ReentrantLock();
-    this.readyCondition = readyLock.newCondition();
-    this.ready = false;
+    this.readyLatch = new java.util.concurrent.CountDownLatch(1);
     this.qos = org.fusesource.mqtt.client.QoS.AT_LEAST_ONCE;
   }
 
@@ -83,7 +148,7 @@ public class MqttHandler {
         String topicString = topic.toString();
         java.util.List<java.util.function.Consumer<byte[]>> callbackList = callbacks.get(topicString);
         if (callbackList == null || callbackList.isEmpty()) {
-          logger.debug("Got a message, but no callback to call. Forgot to unsubscribe?");
+          logger.debug("Got a message, but no callback to call. Forgot to subscribe?");
         } else {
           byte[] message = body.toByteArray();
 //          System.out.println("message = " + Arrays.toString(message));
@@ -144,13 +209,7 @@ public class MqttHandler {
   }
 
   private void setReady() {
-    try {
-      readyLock.lock();
-      ready = true;
-      readyCondition.signalAll();
-    } finally {
-      readyLock.unlock();
-    }
+    readyLatch.countDown();
   }
 
   private void throwIf(java.util.concurrent.atomic.AtomicReference<Throwable> error) throws java.io.IOException {
@@ -163,12 +222,15 @@ public class MqttHandler {
     this.qos = qos;
   }
 
-  public void newConnection(String topic, java.util.function.Consumer<byte[]> callback) {
-    if (!ready) {
-      // should maybe be something more kind than throwing an exception here
-      throw new IllegalStateException("Updater not ready");
+  public boolean newConnection(String topic, java.util.function.Consumer<byte[]> callback) {
+    if (readyLatch.getCount() > 0) {
+      System.err.println("Handler not ready");
+      return false;
+//      // should maybe be something more kind than throwing an exception here
+//      throw new IllegalStateException("Updater not ready");
     }
     // register callback
+    logger.debug("new connection for {}", topic);
     if (callbacks.get(topic) == null) {
       callbacks.put(topic, new java.util.ArrayList<>());
 
@@ -189,6 +251,7 @@ public class MqttHandler {
       });
     }
     callbacks.get(topic).add(callback);
+    return true;
   }
 
   /**
@@ -202,15 +265,9 @@ public class MqttHandler {
    */
   public boolean waitUntilReady(long time, java.util.concurrent.TimeUnit unit) {
     try {
-      readyLock.lock();
-      if (ready) {
-        return true;
-      }
-      return readyCondition.await(time, unit);
+      return readyLatch.await(time, unit);
     } catch (InterruptedException e) {
       e.printStackTrace();
-    } finally {
-      readyLock.unlock();
     }
     return false;
   }
@@ -253,7 +310,7 @@ public class MqttHandler {
 
         @Override
         public void onFailure(Throwable value) {
-          logger.warn("Could not publish on topic '{}'", topic);
+          logger.warn("Could not publish on topic '{}'", topic, value);
         }
       });
     });
diff --git a/src/main/resources/RestHandler.jadd b/src/main/resources/RestHandler.jadd
new file mode 100644
index 0000000000000000000000000000000000000000..f7b1a83304fe7aaebe97d105514c3f192d052aec
--- /dev/null
+++ b/src/main/resources/RestHandler.jadd
@@ -0,0 +1,140 @@
+import java.util.concurrent.TimeUnit;aspect RestHandler {
+public class RestServerHandler {
+  private static final int DEFAULT_PORT = 4567;
+  private final java.util.Map<Integer, RestHandler> handlers = new java.util.HashMap<>();
+  private String name;
+
+  public RestServerHandler() {
+    this("RagConnect");
+  }
+
+  public RestServerHandler(String name) {
+    this.name = name;
+  }
+
+  private RestHandler resolveHandler(java.net.URI uri) {
+    int port = uri.getPort() != -1 ? uri.getPort() : DEFAULT_PORT;
+    RestHandler handler = handlers.get(port);
+    if (handler == null) {
+      // first connect to that server
+      handler = new RestHandler();
+      handler.setPort(port);
+      handlers.put(port, handler);
+    }
+    return handler;
+  }
+
+  public boolean newPUTConnection(java.net.URI uri, java.util.function.Consumer<String> callback) {
+    resolveHandler(uri).newPUTConnection(uri.getPath(), callback);
+    return true;
+  }
+
+  public boolean newGETConnection(java.net.URI uri, SupplierWithException<String> supplier) {
+    resolveHandler(uri).newGETConnection(uri.getPath(), supplier);
+    return true;
+  }
+
+  public void close() {
+    for (RestHandler handler : handlers.values()) {
+      handler.close();
+    }
+  }
+}
+/**
+ * Helper class to receive updates and publishes information via REST.
+ * @author rschoene - Initial contribution
+ */
+public class RestHandler {
+  private static final int DEFAULT_PORT = 4567;
+
+  private final org.apache.logging.log4j.Logger logger;
+  private final String name;
+  private int port;
+  private final java.util.concurrent.CountDownLatch exitCondition;
+  /** Dispatch knowledge */
+  private final java.util.Map<String, java.util.List<java.util.function.Consumer<String>>> callbacks;
+  private final java.util.Map<String, SupplierWithException<String>> suppliers;
+
+  public RestHandler() {
+    this("RagConnect");
+  }
+
+  public RestHandler(String name) {
+    this.logger = org.apache.logging.log4j.LogManager.getLogger(RestHandler.class);
+    this.name = name;
+    this.port = DEFAULT_PORT;
+    this.exitCondition = new java.util.concurrent.CountDownLatch(1);
+    this.callbacks = new java.util.HashMap<>();
+    this.suppliers = new java.util.HashMap<>();
+  }
+
+  public RestHandler setPort(int port) {
+    this.port = port;
+    start();
+    return this;
+  }
+
+  public void newPUTConnection(String path, java.util.function.Consumer<String> callback) {
+    if (callbacks.containsKey(path)) {
+      callbacks.get(path).add(callback);
+    } else {
+      // setup path
+      java.util.List<java.util.function.Consumer<String>> callbackList = new java.util.ArrayList<>();
+      callbackList.add(callback);
+      callbacks.put(path, callbackList);
+      spark.Spark.put(path, (request, response) -> {
+        String content = request.body();
+        java.util.Set<String> errors = new java.util.HashSet<>();
+        for (java.util.function.Consumer<String> f : callbackList) {
+          try {
+            f.accept(content);
+          } catch (Exception e) {
+            errors.add(e.getMessage());
+          }
+        }
+        if (errors.isEmpty()) {
+          return "OK";
+        } else {
+          return makeError(response, 500, errors.stream().collect(java.util.stream.Collectors.joining("\n", "The following error(s) happened: [", "]")));
+        }
+      });
+    }
+  }
+
+  public void newGETConnection(String path, SupplierWithException<String> supplier) {
+    if (suppliers.get(path) != null) {
+      logger.warn("Overriding existing supplier for '{}'", path);
+    }
+    suppliers.put(path, supplier);
+    spark.Spark.get(path, (request, response) -> {
+      try {
+        return supplier.get();
+      } catch (Exception e) {
+        return makeError(response, 500, e.getMessage());
+      }
+    });
+  }
+
+  private String makeError(spark.Response response, int statusCode, String message) {
+    response.status(statusCode);
+    return message;
+  }
+
+  private void start() {
+    logger.info("Starting REST server at {}", this.port);
+    spark.Spark.port(this.port);
+    spark.Spark.init();
+    spark.Spark.awaitInitialization();
+  }
+
+  public void close() {
+    spark.Spark.stop();
+    spark.Spark.awaitStop();
+  }
+
+}
+@FunctionalInterface
+public interface SupplierWithException<T> {
+  public T get() throws Exception;
+}
+}
diff --git a/src/main/resources/dependencyDefinition.mustache b/src/main/resources/dependencyDefinition.mustache
index b8d74ffe8f8aa5aaa8979682e4442215af1974e8..0ab8d55242816a7d3ada0491c8c7db6dcd70ee42 100644
--- a/src/main/resources/dependencyDefinition.mustache
+++ b/src/main/resources/dependencyDefinition.mustache
@@ -1,3 +1,3 @@
-  public void {{targetParentTypeName}}.{{dependencyMethod}}({{sourceParentTypeName}} source) {
-    add{{internalRelationPrefix}}Source(source);
-  }
+public void {{targetParentTypeName}}.{{dependencyMethod}}({{sourceParentTypeName}} source) {
+  add{{internalRelationPrefix}}Source(source);
+}
diff --git a/src/main/resources/handleUri.mustache b/src/main/resources/handleUri.mustache
new file mode 100644
index 0000000000000000000000000000000000000000..1a20e7c4df2a4c54ccba4c825fff55ca89a51dec
--- /dev/null
+++ b/src/main/resources/handleUri.mustache
@@ -0,0 +1,11 @@
+String scheme,host, path;
+java.net.URI uri;
+try {
+  uri = new java.net.URI({{connectParameterName}});
+  scheme = uri.getScheme();
+  host = uri.getHost();
+  path = uri.getPath();
+} catch (java.net.URISyntaxException e) {
+  System.err.println(e.getMessage());  // Maybe re-throw error?
+  return false;
+}
diff --git a/src/main/resources/handler.mustache b/src/main/resources/handler.mustache
new file mode 100644
index 0000000000000000000000000000000000000000..e89c451007ff309a77297b9b66fe0e20dece73b8
--- /dev/null
+++ b/src/main/resources/handler.mustache
@@ -0,0 +1,7 @@
+aspect RagConnectHandler {
+interface RagConnectHandler<T> {
+  boolean connectReceive(String path, java.util.function.Consumer<T> callback);
+  boolean sendPush(String path, T value);
+  boolean connectSendPull(String path, SupplierWithException<T> supplier);
+}
+}
diff --git a/src/main/resources/mappingApplication.mustache b/src/main/resources/mappingApplication.mustache
index 95ce2fe29329d87f880497b37697d50c8c0687be..064004b94886c155be883e7b186557c6fa87d38c 100644
--- a/src/main/resources/mappingApplication.mustache
+++ b/src/main/resources/mappingApplication.mustache
@@ -1,7 +1,7 @@
-{{lastDefinitionToType}} {{resultVarPrefix}}{{lastDefinitionName}};
+{{lastDefinitionToType}} {{lastResult}};
 try {
   {{#InnerMappingDefinitions}}
-  {{^last}}{{ToType}} {{/last}}{{resultVarPrefix}}{{methodName}} = {{methodName}}({{inputVarName}});{{!inputVarName has to be computed beforehand}}
+  {{^last}}{{toType}} {{/last}}{{outputVarName}} = {{methodName}}({{inputVarName}});
   {{/InnerMappingDefinitions}}
 } catch (Exception e) {
   e.printStackTrace();
diff --git a/src/main/resources/mappingDefinition.mustache b/src/main/resources/mappingDefinition.mustache
index 920c5a610b0ce0cff1687122c97b6e18ce63bf7f..fe26e949b7a8e446bf97947e1632624741a83726 100644
--- a/src/main/resources/mappingDefinition.mustache
+++ b/src/main/resources/mappingDefinition.mustache
@@ -1,3 +1,3 @@
-  protected static {{toType}} ASTNode.{{methodName}}({{fromType}} {{fromVariableName}}) throws Exception {
-    {{{content}}}{{!maybe print line by line to get better indentation}}
-  }
+protected static {{toType}} ASTNode.{{methodName}}({{fromType}} {{fromVariableName}}) throws Exception {
+  {{{content}}}{{!maybe print line by line to get better indentation}}
+}
diff --git a/src/main/resources/mqtt.mustache b/src/main/resources/mqtt.mustache
index dbdb85e7954dbf50ae7e150ffc43dd4cbda0fd80..9391d1091c41eee257f9e6a1368297ef743e39ce 100644
--- a/src/main/resources/mqtt.mustache
+++ b/src/main/resources/mqtt.mustache
@@ -1,23 +1,16 @@
 aspect MQTT {
-  private String {{rootNodeName}}.MqttName() { return "Ros2Rag"; }
-  private MqttHandler {{rootNodeName}}.{{mqttHandlerField}} = new MqttHandler(MqttName());
-  public void {{rootNodeName}}.{{mqttSetHostMethod}}(String host) throws java.io.IOException {
-    {{mqttHandlerField}}.setHost(host);
-  }
-  public void {{rootNodeName}}.{{mqttSetHostMethod}}(String host, int port) throws java.io.IOException {
-    {{mqttHandlerField}}.setHost(host, port);
-  }
-
-  public boolean {{rootNodeName}}.{{mqttWaitUntilReadyMethod}}(long time, java.util.concurrent.TimeUnit unit) {
-    return {{mqttHandlerField}}.waitUntilReady(time, unit);
-  }
+  private String {{rootNodeName}}.MqttName() { return "RagConnectMQTT"; }
+  private MqttServerHandler {{rootNodeName}}.{{mqttHandlerField}} = new MqttServerHandler(MqttName());
 
-  public void {{rootNodeName}}.{{mqttCloseMethod}}() {
-    {{mqttHandlerField}}.close();
+  public void {{rootNodeName}}.{{mqttSetupWaitUntilReadyMethod}}(long time, java.util.concurrent.TimeUnit unit) {
+    {{mqttHandlerField}}.setupWaitUntilReady(time, unit);
   }
 
-  {{#getRootTypeComponents}}
-  {{#first}}inh MqttHandler ASTNode.{{mqttHandlerAttribute}}();{{/first}}
+  {{#RootTypeComponents}}
+  {{#first}}inh MqttServerHandler ASTNode.{{mqttHandlerAttribute}}();{{/first}}
   eq {{rootNodeName}}.get{{name}}().{{mqttHandlerAttribute}}() = {{mqttHandlerField}};
-  {{/getRootTypeComponents}}
+  {{/RootTypeComponents}}
+  {{^RootTypeComponents}}
+  syn MqttServerHandler {{rootNodeName}}.{{mqttHandlerAttribute}}() = {{mqttHandlerField}};
+  {{/RootTypeComponents}}
 }
diff --git a/src/main/resources/ragconnect.mustache b/src/main/resources/ragconnect.mustache
index 0b455283977cf812a47b5266ee593c6597e97cc9..a39510d73854301efb1f05fe7f9c1fd476916ef7 100644
--- a/src/main/resources/ragconnect.mustache
+++ b/src/main/resources/ragconnect.mustache
@@ -1,22 +1,27 @@
-{{> mqtt}}
+{{#usesMqtt}}{{> mqtt}}{{/usesMqtt}}
+{{#usesRest}}{{> rest}}{{/usesRest}}
 aspect ROS2RAG {
+  public void {{rootNodeName}}.{{closeMethod}}() {
+    {{#usesMqtt}}{{mqttHandlerField}}.close();{{/usesMqtt}}
+    {{#usesRest}}{{restHandlerField}}.close();{{/usesRest}}
+  }
   {{#ReceiveDefinitions}}
-    {{> receiveDefinition}}
+  {{> receiveDefinition}}
   {{/ReceiveDefinitions}}
 
   {{#SendDefinitions}}
-    {{> sendDefinition}}
+  {{> sendDefinition}}
   {{/SendDefinitions}}
 
   {{#MappingDefinitions}}
-    {{> mappingDefinition}}
+  {{> mappingDefinition}}
   {{/MappingDefinitions}}
 
   {{#DependencyDefinitions}}
-    {{> dependencyDefinition}}
+  {{> dependencyDefinition}}
   {{/DependencyDefinitions}}
 
   {{#TokenComponents}}
-    {{> tokenComponent}}
+  {{> tokenComponent}}
   {{/TokenComponents}}
 }
diff --git a/src/main/resources/receiveDefinition.mustache b/src/main/resources/receiveDefinition.mustache
index e2df0433fe82b37df2c016887da52a11cfd2692b..1dd0e0ddbfbfd2acf03536ff41a2d740043eb34b 100644
--- a/src/main/resources/receiveDefinition.mustache
+++ b/src/main/resources/receiveDefinition.mustache
@@ -1,9 +1,23 @@
-  public void {{parentTypeName}}.{{connectMethod}}(String topic) {
-    {{mqttHandlerAttribute}}().newConnection(topic, message -> {
-      {{> mappingApplication}}
-      {{#loggingEnabledForReads}}
-      System.out.println("[Receive] " + topic + " -> {{tokenName}} = " + {{lastResult}});{{!lastResult has to be a new attribute}}
-      {{/loggingEnabledForReads}}
-      set{{tokenName}}({{lastResult}});
+public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}) throws java.io.IOException {
+  {{>handleUri}}
+  java.util.function.Consumer<byte[]> consumer = message -> {
+    {{> mappingApplication}}
+    {{#loggingEnabledForReads}}
+    System.out.println("[Receive] " + {{connectParameterName}} + " -> {{tokenName}} = " + {{lastResult}});
+    {{/loggingEnabledForReads}}
+    set{{tokenName}}({{lastResult}});
+  };
+  switch (scheme) {
+  {{#usesMqtt}}
+    case "mqtt": return {{mqttHandlerAttribute}}().newConnection(uri, consumer);
+  {{/usesMqtt}}
+  {{#usesRest}}
+    case "rest": return {{restHandlerAttribute}}().newPUTConnection(uri, input -> {
+      consumer.accept(input.getBytes());
     });
+  {{/usesRest}}
+    default:
+      System.err.println("Unknown protocol '" + scheme + "'.");
+      return false;
   }
+}
diff --git a/src/main/resources/rest.mustache b/src/main/resources/rest.mustache
new file mode 100644
index 0000000000000000000000000000000000000000..6bbde2be4d26e519635c85a6f39f9a18514bfb19
--- /dev/null
+++ b/src/main/resources/rest.mustache
@@ -0,0 +1,12 @@
+aspect REST {
+  private String {{rootNodeName}}.RestName() { return "RagConnectREST"; }
+  private RestServerHandler {{rootNodeName}}.{{restHandlerField}} = new RestServerHandler(RestName());
+
+  {{#getRootTypeComponents}}
+  {{#first}}inh RestServerHandler ASTNode.{{restHandlerAttribute}}();{{/first}}
+  eq {{rootNodeName}}.get{{name}}().{{restHandlerAttribute}}() = {{restHandlerField}};
+  {{/getRootTypeComponents}}
+  {{^getRootTypeComponents}}
+  syn RestServerHandler {{rootNodeName}}.{{restHandlerAttribute}}() = {{restHandlerField}};
+  {{/getRootTypeComponents}}
+}
diff --git a/src/main/resources/sendDefinition.mustache b/src/main/resources/sendDefinition.mustache
index 477dbc550132af842e53e8fed2cbbee0c8996296..fac4ecd37967b8047c9101b58fab273527979ab2 100644
--- a/src/main/resources/sendDefinition.mustache
+++ b/src/main/resources/sendDefinition.mustache
@@ -1,24 +1,51 @@
-  private String {{parentTypeName}}.{{sendTopic}} = null;
-  private byte[] {{parentTypeName}}.{{lastValue}} = null;
+private Runnable {{parentTypeName}}.{{sender}} = null;
+private byte[] {{parentTypeName}}.{{lastValue}} = null;
 
-  public void {{parentTypeName}}.{{connectMethod}}(String topic, boolean writeCurrentValue) {
-    {{sendTopic}} = topic;
-    {{updateMethod}}();
-    if (writeCurrentValue) {
-      {{writeMethod}}();
-    }
+public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}, boolean writeCurrentValue) {
+  {{>handleUri}}
+  switch (scheme) {
+  {{#usesMqtt}}
+    case "mqtt":
+      // MqttHandler handler = {{mqttHandlerAttribute}}().resolveHandler(uri);{{!optimize later}}
+      {{sender}} = () -> {
+        {{#loggingEnabledForWrites}}
+        System.out.println("[Send] {{tokenName}} = " + get{{tokenName}}() + " -> " + {{connectParameterName}});
+        {{/loggingEnabledForWrites}}
+        try {
+          {{mqttHandlerAttribute}}().publish(uri, {{lastValue}});
+        } catch (java.io.IOException e) {
+          e.printStackTrace();
+        }
+      };
+      {{updateMethod}}();
+      if (writeCurrentValue) {
+        {{writeMethod}}();
+      }
+      break;
+  {{/usesMqtt}}
+  {{#usesRest}}
+    case "rest":
+      {{restHandlerAttribute}}().newGETConnection(uri, () -> {
+        {{updateMethod}}();
+        return new String({{lastValue}});
+      });
+      break;
+  {{/usesRest}}
+    default:
+      System.err.println("Unknown protocol '" + scheme + "'.");
+      return false;
   }
+  return true;
+}
 
-  protected boolean {{parentTypeName}}.{{updateMethod}}() {
-    {{tokenResetMethod}}();
-    {{> mappingApplication}}
-    {{lastValue}} = {{lastResult}};
-    return true;
-  }
+protected boolean {{parentTypeName}}.{{updateMethod}}() {
+  {{tokenResetMethod}}();
+  {{> mappingApplication}}
+  {{lastValue}} = {{lastResult}};
+  // normally we would return true here. unless no connect method was called so far to initialize {{sender}} yet
+  return {{sender}} != null;
+}
 
-  protected void {{parentTypeName}}.{{writeMethod}}() {
-    {{#loggingEnabledForWrites}}
-    System.out.println("[Send] {{tokenName}} = " + get{{tokenName}}() + " -> " + {{sendTopic}});
-    {{/loggingEnabledForWrites}}
-    {{mqttHandlerAttribute}}().publish({{sendTopic}}, {{lastValue}});
-  }
+protected void {{parentTypeName}}.{{writeMethod}}() {
+  {{sender}}.run();
+}
diff --git a/src/main/resources/tokenComponent.mustache b/src/main/resources/tokenComponent.mustache
index e6a136d1c8de08b6dce9104fd46fafa5d91b67b6..e78efb96b846bd7aa814723472032299a70c7de3 100644
--- a/src/main/resources/tokenComponent.mustache
+++ b/src/main/resources/tokenComponent.mustache
@@ -1,17 +1,19 @@
-  public {{parentTypeName}} {{parentTypeName}}.set{{name}}({{javaType}} value) {
-    set{{internalName}}(value);
-    {{#DependencyDefinitions}}
-    for ({{targetParentTypeName}} target : get{{internalRelationPrefix}}TargetList()) {
-      {{#targetEndpointDefinition}}
-      if (target.{{updateMethod}}()) {
-        target.{{writeMethod}}();
-      }
-      {{/targetEndpointDefinition}}
+public {{parentTypeName}} {{parentTypeName}}.set{{name}}({{javaType}} value) {
+  set{{internalName}}(value);
+  {{#DependencyDefinitions}}
+  for ({{targetParentTypeName}} target : get{{internalRelationPrefix}}TargetList()) {
+    {{#targetEndpointDefinition}}
+    {{!#isPush}}
+    if (target.{{updateMethod}}()) {
+      target.{{writeMethod}}();
     }
-    {{/DependencyDefinitions}}
-    return this;
+    {{!/isPush}}
+    {{/targetEndpointDefinition}}
   }
+  {{/DependencyDefinitions}}
+  return this;
+}
 
-  public {{javaType}} {{parentTypeName}}.get{{name}}() {
-    return get{{internalName}}();
-  }
+public {{javaType}} {{parentTypeName}}.get{{name}}() {
+  return get{{internalName}}();
+}