diff --git a/ConductorSharp.sln b/ConductorSharp.sln
index f0f8822f..94732eff 100644
--- a/ConductorSharp.sln
+++ b/ConductorSharp.sln
@@ -1,73 +1,79 @@
-﻿
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.0.31919.166
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConductorSharp.Engine", "src\ConductorSharp.Engine\ConductorSharp.Engine.csproj", "{22BDC06E-50E5-4FE4-97D2-CD9E07EF4842}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConductorSharp.Client", "src\ConductorSharp.Client\ConductorSharp.Client.csproj", "{3244BEE8-17C4-4B00-BEB1-A41CC3E95482}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConductorSharp.ApiEnabled", "examples\ConductorSharp.ApiEnabled\ConductorSharp.ApiEnabled.csproj", "{C350E5E2-EA36-4816-BCBB-CBB597AAFF78}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConductorSharp.Definitions", "examples\ConductorSharp.Definitions\ConductorSharp.Definitions.csproj", "{68400F84-CE5A-4FFA-A417-05C798ECD5A4}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{16F68738-1ED3-4646-BE01-DB9246C10E89}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConductorSharp.NoApi", "examples\ConductorSharp.NoApi\ConductorSharp.NoApi.csproj", "{44E887F8-5E0D-4729-A4A8-AFEBBF87C6F9}"
-EndProject
-Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{C398B7D9-C238-4169-86A8-B03D01FBB2BE}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConductorSharp.Engine.Tests", "test\ConductorSharp.Engine.Tests\ConductorSharp.Engine.Tests.csproj", "{45AB7886-41DB-4A0E-9667-1C78D63F0FC7}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DE1FA421-B8D6-45A7-9667-0798A241A89C}"
-	ProjectSection(SolutionItems) = preProject
-		.editorconfig = .editorconfig
-	EndProjectSection
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		Debug|Any CPU = Debug|Any CPU
-		Release|Any CPU = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{22BDC06E-50E5-4FE4-97D2-CD9E07EF4842}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{22BDC06E-50E5-4FE4-97D2-CD9E07EF4842}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{22BDC06E-50E5-4FE4-97D2-CD9E07EF4842}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{22BDC06E-50E5-4FE4-97D2-CD9E07EF4842}.Release|Any CPU.Build.0 = Release|Any CPU
-		{3244BEE8-17C4-4B00-BEB1-A41CC3E95482}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{3244BEE8-17C4-4B00-BEB1-A41CC3E95482}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{3244BEE8-17C4-4B00-BEB1-A41CC3E95482}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{3244BEE8-17C4-4B00-BEB1-A41CC3E95482}.Release|Any CPU.Build.0 = Release|Any CPU
-		{C350E5E2-EA36-4816-BCBB-CBB597AAFF78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{C350E5E2-EA36-4816-BCBB-CBB597AAFF78}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{C350E5E2-EA36-4816-BCBB-CBB597AAFF78}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{C350E5E2-EA36-4816-BCBB-CBB597AAFF78}.Release|Any CPU.Build.0 = Release|Any CPU
-		{68400F84-CE5A-4FFA-A417-05C798ECD5A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{68400F84-CE5A-4FFA-A417-05C798ECD5A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{68400F84-CE5A-4FFA-A417-05C798ECD5A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{68400F84-CE5A-4FFA-A417-05C798ECD5A4}.Release|Any CPU.Build.0 = Release|Any CPU
-		{44E887F8-5E0D-4729-A4A8-AFEBBF87C6F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{44E887F8-5E0D-4729-A4A8-AFEBBF87C6F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{44E887F8-5E0D-4729-A4A8-AFEBBF87C6F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{44E887F8-5E0D-4729-A4A8-AFEBBF87C6F9}.Release|Any CPU.Build.0 = Release|Any CPU
-		{C398B7D9-C238-4169-86A8-B03D01FBB2BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{C398B7D9-C238-4169-86A8-B03D01FBB2BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{C398B7D9-C238-4169-86A8-B03D01FBB2BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{C398B7D9-C238-4169-86A8-B03D01FBB2BE}.Release|Any CPU.Build.0 = Release|Any CPU
-		{45AB7886-41DB-4A0E-9667-1C78D63F0FC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{45AB7886-41DB-4A0E-9667-1C78D63F0FC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{45AB7886-41DB-4A0E-9667-1C78D63F0FC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{45AB7886-41DB-4A0E-9667-1C78D63F0FC7}.Release|Any CPU.Build.0 = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-	GlobalSection(NestedProjects) = preSolution
-		{C350E5E2-EA36-4816-BCBB-CBB597AAFF78} = {16F68738-1ED3-4646-BE01-DB9246C10E89}
-		{68400F84-CE5A-4FFA-A417-05C798ECD5A4} = {16F68738-1ED3-4646-BE01-DB9246C10E89}
-		{44E887F8-5E0D-4729-A4A8-AFEBBF87C6F9} = {16F68738-1ED3-4646-BE01-DB9246C10E89}
-	EndGlobalSection
-	GlobalSection(ExtensibilityGlobals) = postSolution
-		SolutionGuid = {2443B3EC-E590-4C83-8BE2-A0787C66307C}
-	EndGlobalSection
-EndGlobal
+﻿
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31919.166
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConductorSharp.Engine", "src\ConductorSharp.Engine\ConductorSharp.Engine.csproj", "{22BDC06E-50E5-4FE4-97D2-CD9E07EF4842}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConductorSharp.Client", "src\ConductorSharp.Client\ConductorSharp.Client.csproj", "{3244BEE8-17C4-4B00-BEB1-A41CC3E95482}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConductorSharp.ApiEnabled", "examples\ConductorSharp.ApiEnabled\ConductorSharp.ApiEnabled.csproj", "{C350E5E2-EA36-4816-BCBB-CBB597AAFF78}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConductorSharp.Definitions", "examples\ConductorSharp.Definitions\ConductorSharp.Definitions.csproj", "{68400F84-CE5A-4FFA-A417-05C798ECD5A4}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{16F68738-1ED3-4646-BE01-DB9246C10E89}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConductorSharp.NoApi", "examples\ConductorSharp.NoApi\ConductorSharp.NoApi.csproj", "{44E887F8-5E0D-4729-A4A8-AFEBBF87C6F9}"
+EndProject
+Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{C398B7D9-C238-4169-86A8-B03D01FBB2BE}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConductorSharp.Engine.Tests", "test\ConductorSharp.Engine.Tests\ConductorSharp.Engine.Tests.csproj", "{45AB7886-41DB-4A0E-9667-1C78D63F0FC7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConductorSharp.Toolkit", "src\ConductorSharp.Toolkit\ConductorSharp.Toolkit.csproj", "{7FC97985-07CE-4643-8415-C48BA2117D88}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DE1FA421-B8D6-45A7-9667-0798A241A89C}"
+	ProjectSection(SolutionItems) = preProject
+		.editorconfig = .editorconfig
+	EndProjectSection
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{22BDC06E-50E5-4FE4-97D2-CD9E07EF4842}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{22BDC06E-50E5-4FE4-97D2-CD9E07EF4842}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{22BDC06E-50E5-4FE4-97D2-CD9E07EF4842}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{22BDC06E-50E5-4FE4-97D2-CD9E07EF4842}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3244BEE8-17C4-4B00-BEB1-A41CC3E95482}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3244BEE8-17C4-4B00-BEB1-A41CC3E95482}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3244BEE8-17C4-4B00-BEB1-A41CC3E95482}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3244BEE8-17C4-4B00-BEB1-A41CC3E95482}.Release|Any CPU.Build.0 = Release|Any CPU
+		{C350E5E2-EA36-4816-BCBB-CBB597AAFF78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{C350E5E2-EA36-4816-BCBB-CBB597AAFF78}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{C350E5E2-EA36-4816-BCBB-CBB597AAFF78}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{C350E5E2-EA36-4816-BCBB-CBB597AAFF78}.Release|Any CPU.Build.0 = Release|Any CPU
+		{68400F84-CE5A-4FFA-A417-05C798ECD5A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{68400F84-CE5A-4FFA-A417-05C798ECD5A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{68400F84-CE5A-4FFA-A417-05C798ECD5A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{68400F84-CE5A-4FFA-A417-05C798ECD5A4}.Release|Any CPU.Build.0 = Release|Any CPU
+		{44E887F8-5E0D-4729-A4A8-AFEBBF87C6F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{44E887F8-5E0D-4729-A4A8-AFEBBF87C6F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{44E887F8-5E0D-4729-A4A8-AFEBBF87C6F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{44E887F8-5E0D-4729-A4A8-AFEBBF87C6F9}.Release|Any CPU.Build.0 = Release|Any CPU
+		{C398B7D9-C238-4169-86A8-B03D01FBB2BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{C398B7D9-C238-4169-86A8-B03D01FBB2BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{C398B7D9-C238-4169-86A8-B03D01FBB2BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{C398B7D9-C238-4169-86A8-B03D01FBB2BE}.Release|Any CPU.Build.0 = Release|Any CPU
+		{45AB7886-41DB-4A0E-9667-1C78D63F0FC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{45AB7886-41DB-4A0E-9667-1C78D63F0FC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{45AB7886-41DB-4A0E-9667-1C78D63F0FC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{45AB7886-41DB-4A0E-9667-1C78D63F0FC7}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7FC97985-07CE-4643-8415-C48BA2117D88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7FC97985-07CE-4643-8415-C48BA2117D88}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7FC97985-07CE-4643-8415-C48BA2117D88}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7FC97985-07CE-4643-8415-C48BA2117D88}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{C350E5E2-EA36-4816-BCBB-CBB597AAFF78} = {16F68738-1ED3-4646-BE01-DB9246C10E89}
+		{68400F84-CE5A-4FFA-A417-05C798ECD5A4} = {16F68738-1ED3-4646-BE01-DB9246C10E89}
+		{44E887F8-5E0D-4729-A4A8-AFEBBF87C6F9} = {16F68738-1ED3-4646-BE01-DB9246C10E89}
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {2443B3EC-E590-4C83-8BE2-A0787C66307C}
+	EndGlobalSection
+EndGlobal
diff --git a/src/ConductorSharp.Toolkit/Commands/Command.cs b/src/ConductorSharp.Toolkit/Commands/Command.cs
new file mode 100644
index 00000000..165f6e79
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/Commands/Command.cs
@@ -0,0 +1,10 @@
+﻿using ConductorSharp.Toolkit.Models;
+
+namespace ConductorSharp.Toolkit.Commands
+{
+    public interface Command
+    {
+        public string GetName();
+        public Task Execute(CommandInput input);
+    }
+}
diff --git a/src/ConductorSharp.Toolkit/Commands/CommandRegistry.cs b/src/ConductorSharp.Toolkit/Commands/CommandRegistry.cs
new file mode 100644
index 00000000..3e2cdd7c
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/Commands/CommandRegistry.cs
@@ -0,0 +1,14 @@
+﻿namespace ConductorSharp.Toolkit.Commands
+{
+    public class CommandRegistry
+    {
+        private readonly IEnumerable<Command> _commands;
+
+        public CommandRegistry(IEnumerable<Command> commands)
+        {
+            _commands = commands;
+        }
+
+        public Command Get(string name) => _commands.FirstOrDefault(a => a.GetName() == name);
+    }
+}
diff --git a/src/ConductorSharp.Toolkit/Commands/ScaffoldCommand.cs b/src/ConductorSharp.Toolkit/Commands/ScaffoldCommand.cs
new file mode 100644
index 00000000..9f57c8a0
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/Commands/ScaffoldCommand.cs
@@ -0,0 +1,20 @@
+﻿using ConductorSharp.Toolkit.Models;
+using ConductorSharp.Toolkit.Service;
+
+namespace ConductorSharp.Toolkit.Commands
+{
+    public class ScaffoldCommand : Command
+    {
+        private readonly IScaffoldingService _scaffoldingService;
+        public ScaffoldCommand(IScaffoldingService scaffoldingService)
+        {
+            _scaffoldingService = scaffoldingService;
+        }
+
+        public string GetName() => "scaffold";
+        public async Task Execute(CommandInput input)
+        {
+            await _scaffoldingService.Scaffold();
+        }
+    }
+}
diff --git a/src/ConductorSharp.Toolkit/ConductorSharp.Toolkit.csproj b/src/ConductorSharp.Toolkit/ConductorSharp.Toolkit.csproj
new file mode 100644
index 00000000..bdc8bcc6
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/ConductorSharp.Toolkit.csproj
@@ -0,0 +1,36 @@
+﻿<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>disable</Nullable>
+	<PackAsTool>true</PackAsTool>
+	<ToolCommandName>dotnet-conductorsharp</ToolCommandName>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <None Remove="Templates\TaskCollectionTemplate.default" />
+    <None Remove="Templates\WorkerTemplate.default" />
+    <None Remove="Templates\WorkflowCollectionTemplate.default" />
+    <None Remove="Templates\WorkflowTemplate.default" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <EmbeddedResource Include="Templates\TaskCollectionTemplate.default" />
+    <EmbeddedResource Include="Templates\WorkerTemplate.default" />
+    <EmbeddedResource Include="Templates\WorkflowCollectionTemplate.default" />
+    <EmbeddedResource Include="Templates\WorkflowTemplate.default" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
+    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
+    <PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\ConductorSharp.Engine\ConductorSharp.Engine.csproj" />
+  </ItemGroup>
+
+</Project>
diff --git a/src/ConductorSharp.Toolkit/Models/CommandInput.cs b/src/ConductorSharp.Toolkit/Models/CommandInput.cs
new file mode 100644
index 00000000..19b2d75d
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/Models/CommandInput.cs
@@ -0,0 +1,11 @@
+﻿namespace ConductorSharp.Toolkit.Models
+{
+    public class CommandInput
+    {
+        public string Api { get; set; }
+        public string Namespace { get; set; }
+        public string Host { get; set; }
+        public bool Dryrun { get; set; }
+        public string Destination { get; set; }
+    }
+}
diff --git a/src/ConductorSharp.Toolkit/Models/WorkerDocument.cs b/src/ConductorSharp.Toolkit/Models/WorkerDocument.cs
new file mode 100644
index 00000000..53cc626f
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/Models/WorkerDocument.cs
@@ -0,0 +1,25 @@
+﻿using System.Collections;
+
+namespace ConductorSharp.Toolkit.Models
+{
+    public class WorkerDocument
+    {
+        public WorkerDocument()
+        {
+            InputTable = new List<string>();
+            OutputTable = new List<string>();
+            UpdatedWorkers = new Hashtable();
+        }
+
+        public string SourcePath { get; set; }
+        public string DestinationPath { get; set; }
+        public string WorkerName { get; set; }
+        public string WorkerDescription { get; set; } //short description of worker
+        public List<string> InputTable { get; set; }
+        public List<string> OutputTable { get; set; }
+        public Hashtable UpdatedWorkers { get; set; } //to prevent us from adding the same task twice or more, because it can happen that several workflows use the same worker
+        public string OwnerApp { get; set; }
+        public string OwnerEmail { get; set; }
+        public string RegistrationTime { get; set; }
+    }
+}
diff --git a/src/ConductorSharp.Toolkit/Models/WorkflowDocument.cs b/src/ConductorSharp.Toolkit/Models/WorkflowDocument.cs
new file mode 100644
index 00000000..39bf7b9a
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/Models/WorkflowDocument.cs
@@ -0,0 +1,19 @@
+﻿namespace ConductorSharp.Toolkit.Models
+{
+    public class WorkflowDocument
+    {
+        public string SourcePath { get; set; } //path to .md file of the workflow
+        public string DestinationPath { get; set; }
+        public string WorkflowName { get; set; }
+        public string WorkflowDescription { get; set; } //short description of workflow
+        public List<string> DiagramCode { get; set; }
+        public List<string> InputTable { get; set; }
+        public List<string> TaskTable { get; set; }
+        public List<string> OutputTable { get; set; }
+        public string OwnerApp { get; set; }
+        public string OwnerEmail { get; set; }
+        public string Version { get; set; }
+        public string FailureWorkflow { get; set; }
+        public string RegistrationTime { get; set; }
+    }
+}
diff --git a/src/ConductorSharp.Toolkit/Program.cs b/src/ConductorSharp.Toolkit/Program.cs
new file mode 100644
index 00000000..38bae6f9
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/Program.cs
@@ -0,0 +1,77 @@
+﻿using Autofac;
+using Autofac.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection;
+using ConductorSharp.Engine.Extensions;
+using ConductorSharp.Toolkit.Commands;
+using ConductorSharp.Toolkit.Models;
+
+namespace ConductorSharp.Toolkit
+{
+    class Program
+    {
+        public async static Task Main(string[] args)
+        {
+            try
+            {
+                var action = args[0];
+
+                var input = ParseInput(args);
+
+                var container = BuildContainer(input);
+
+                var commandRegistry = container.Resolve<CommandRegistry>();
+
+                var command = commandRegistry.Get(action);
+
+                await command.Execute(input);
+            }
+            catch (Exception exc)
+            {
+                PrintHelp();
+            }
+        }
+
+        private static void PrintHelp() => Console.WriteLine("PLACEHOLDER HELP");
+
+        private static IContainer BuildContainer(CommandInput input)
+        {
+            var serviceCollection = new ServiceCollection();
+
+            serviceCollection.AddLogging();
+
+            var builder = new ContainerBuilder();
+
+            serviceCollection.Configure<ScaffoldingConfig>(
+                config =>
+                {
+                    config.ApiUrl = input.Api;
+                    config.BaseUrl = input.Host;
+                    config.BaseNamespace = input.Namespace;
+                    config.Dryrun = input.Dryrun;
+                    config.Destination = input.Destination;
+                }
+            );
+
+            builder.Populate(serviceCollection);
+            builder.AddWorkflowEngine(input.Host, input.Api);
+            builder.RegisterModule(new ToolkitModule());
+
+            return builder.Build();
+        }
+
+        private static CommandInput ParseInput(string[] args)
+        {
+            var action = args[0];
+            var inputParameters = args.Skip(1).Select(a => new KeyValuePair<string, string>(a.Split("=")[0], a.Split("=")[1])).ToList();
+
+            return new CommandInput
+            {
+                Api = inputParameters.Where(a => a.Key == "path").Select(a => a.Value).FirstOrDefault(),
+                Namespace = inputParameters.Where(a => a.Key == "namespace").Select(a => a.Value).FirstOrDefault(),
+                Host = inputParameters.Where(a => a.Key == "host").Select(a => a.Value).FirstOrDefault(),
+                Dryrun = inputParameters.Where(a => a.Key == "dryrun").Select(a => bool.Parse(a.Value)).FirstOrDefault(),
+                Destination = inputParameters.Where(a => a.Key == "destination").Select(a => a.Value).FirstOrDefault()
+            };
+        }
+    }
+}
diff --git a/src/ConductorSharp.Toolkit/ScaffoldingConfig.cs b/src/ConductorSharp.Toolkit/ScaffoldingConfig.cs
new file mode 100644
index 00000000..3cc20542
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/ScaffoldingConfig.cs
@@ -0,0 +1,11 @@
+﻿namespace ConductorSharp.Toolkit
+{
+    public class ScaffoldingConfig
+    {
+        public string BaseNamespace { get; set; }
+        public string ApiUrl { get; set; }
+        public string BaseUrl { get; set; }
+        public bool Dryrun { get; set; }
+        public string Destination { get; set; }
+    }
+}
diff --git a/src/ConductorSharp.Toolkit/Service/IScaffoldingService.cs b/src/ConductorSharp.Toolkit/Service/IScaffoldingService.cs
new file mode 100644
index 00000000..5dca7b3a
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/Service/IScaffoldingService.cs
@@ -0,0 +1,7 @@
+﻿namespace ConductorSharp.Toolkit.Service
+{
+    public interface IScaffoldingService
+    {
+        Task Scaffold();
+    }
+}
diff --git a/src/ConductorSharp.Toolkit/Service/ScaffoldingService.cs b/src/ConductorSharp.Toolkit/Service/ScaffoldingService.cs
new file mode 100644
index 00000000..9460bd8f
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/Service/ScaffoldingService.cs
@@ -0,0 +1,271 @@
+﻿using ConductorSharp.Client.Model.Common;
+using ConductorSharp.Client.Service;
+using ConductorSharp.Engine.Util;
+using ConductorSharp.Toolkit.Util;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Newtonsoft.Json.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace ConductorSharp.Toolkit.Service
+{
+    public class ScaffoldingService : IScaffoldingService
+    {
+        private readonly IMetadataService _metadataService;
+        private readonly ILogger<ScaffoldingService> _logger;
+        private readonly ScaffoldingConfig _config;
+
+        public ScaffoldingService(IMetadataService metadataService, IOptions<ScaffoldingConfig> options, ILogger<ScaffoldingService> logger)
+        {
+            _metadataService = metadataService;
+            _logger = logger;
+            _config = options.Value;
+        }
+
+        public async Task Scaffold()
+        {
+            var workflowDefinitions = await _metadataService.GetAllWorkflowDefinitions();
+            var workflowCollectionBuilder = new StringBuilder();
+            foreach (var workflowDefinition in workflowDefinitions)
+            {
+                var workflowClass = CreateWorkflowClass(workflowDefinition);
+
+                if (workflowClass != null)
+                    workflowCollectionBuilder.Append(workflowClass);
+            }
+
+            var taskDefinitions = await _metadataService.GetAllTaskDefinitions();
+
+            var taskCollectionBuilder = new StringBuilder();
+            foreach (var taskDefinition in taskDefinitions)
+            {
+                var taskClass = CreateTaskClass(taskDefinition);
+
+                if (taskClass != null)
+                    taskCollectionBuilder.Append(taskClass);
+            }
+
+            var taskTemplate = EmbeddedFileHelper.GetLinesFromEmbeddedFile("~/Templates/TaskCollectionTemplate.default");
+            var taskCollection = taskCollectionBuilder.ToString();
+            taskTemplate = taskTemplate.Replace("{{namespace}}", _config.BaseNamespace + ".Tasks");
+            taskTemplate = taskTemplate.Replace("{{taskCollection}}", taskCollection);
+            var dir = _config.Destination + Path.DirectorySeparatorChar + "Tasks";
+
+            Directory.CreateDirectory(dir);
+
+            File.WriteAllText(dir + Path.DirectorySeparatorChar + "Tasks.scaff.cs", taskTemplate);
+
+            var workflowTemplate = EmbeddedFileHelper.GetLinesFromEmbeddedFile("~/Templates/WorkflowCollectionTemplate.default");
+            var workflowCollection = workflowCollectionBuilder.ToString();
+            workflowTemplate = workflowTemplate.Replace("{{namespace}}", _config.BaseNamespace + ".Workflows");
+            workflowTemplate = workflowTemplate.Replace("{{workflowCollection}}", workflowCollection);
+            var workflowDir = _config.Destination + Path.DirectorySeparatorChar + "Workflows";
+
+            Directory.CreateDirectory(workflowDir);
+
+            File.WriteAllText(workflowDir + Path.DirectorySeparatorChar + "Workflows.scaff.cs", workflowTemplate);
+        }
+
+        public string CreateWorkflowClass(WorkflowDefinition workflowDefinition)
+        {
+            var lines = EmbeddedFileHelper.GetLinesFromEmbeddedFile("~/Templates/WorkflowTemplate.default");
+
+            string name = SnakeCaseUtil.ToPascalCase($"{workflowDefinition.Name}_V{workflowDefinition.Version}").Trim();
+            string note = null;
+            if (LangUtils.MakeValidMemberName(name, "A", out name))
+            {
+                note = "The autogenerated name has been changed because it is invalid C# member name";
+            }
+
+            string workflowDescription = workflowDefinition.Description;
+            string[] labels;
+
+            try
+            {
+                var descriptionObject = JObject.Parse(workflowDescription);
+                workflowDescription = descriptionObject.SelectToken("description")?.Value<string>();
+                var labelsObject = descriptionObject.SelectToken("labels");
+
+                if (labelsObject is JArray labelsArray)
+                {
+                    labels = labelsArray.ToObject<string[]>();
+                }
+            }
+            catch (Exception)
+            {
+                _logger.LogWarning("Unable to parse description for workflow {0}", workflowDefinition.Name);
+            }
+
+            var inputLinesBuilder = new StringBuilder();
+            try
+            {
+                var inputParameters = JObject.Parse(workflowDefinition.InputParametersJSON[0]);
+
+                foreach (var param in inputParameters)
+                {
+                    var value = param.Value.SelectToken("value")?.ToString(Newtonsoft.Json.Formatting.None);
+                    var type = param.Value.SelectToken("type")?.Value<string>();
+                    var description = param.Value.SelectToken("description")?.Value<string>();
+
+                    if (type == null)
+                        type = "string";
+
+                    AppendXmlComment(inputLinesBuilder, "originalName", param.Key);
+
+                    if (!string.IsNullOrEmpty(description))
+                        AppendXmlComment(inputLinesBuilder, "summary", value);
+                    
+                    switch (type)
+                    {
+                        case "string":
+                        case "integer":
+                            if (!string.IsNullOrEmpty(value))
+                            {
+                                AppendXmlComment(inputLinesBuilder, "remark", $"Example: {value}");
+                            }
+                            AppendClassProperty(inputLinesBuilder, "dynamic", param.Key);
+                            break;
+                        case "toggle":
+                            AppendClassProperty(inputLinesBuilder, "dynamic", param.Key);
+                            break;
+                        case "select":
+                            var optionsToken = param.Value.SelectToken("options");
+
+                            string[] options = null;
+
+                            if (optionsToken is JArray optionsArray)
+                            {
+                                options = optionsArray.ToObject<string[]>();
+                            }
+                            if (options != null && options.Length > 0)
+                                AppendXmlComment(inputLinesBuilder, "remark", $"Options: {string.Join(',', options)}");
+                            
+                            AppendClassProperty(inputLinesBuilder, "dynamic", param.Key);
+                            break;
+                        default:
+                            break;
+                    }
+                }
+            }
+            catch (Exception)
+            {
+                if (workflowDefinition.InputParametersJSON != null)
+                    Console.WriteLine(
+                        $"Unable to parse input parameters {string.Join(',', workflowDefinition.InputParametersJSON)} for {workflowDefinition.Name})"
+                    );
+                else
+                    Console.WriteLine($"No input parameters defined for {workflowDefinition.Name}");
+            }
+
+            if (string.IsNullOrEmpty(workflowDefinition.OwnerEmail))
+                _logger.LogWarning($"No owner email defined for task {workflowDefinition.Name}");
+
+            if (string.IsNullOrEmpty(workflowDefinition.OwnerApp))
+                _logger.LogWarning($"No owner app defined for task {workflowDefinition.Name}");
+
+            lines = lines.Replace("{{inputProperties}}", inputLinesBuilder.ToString());
+            lines = lines.Replace("{{workflowName}}", name);
+            lines = lines.Replace("{{note}}", note);
+            lines = lines.Replace("{{ownerEmail}}", $"{workflowDefinition.OwnerEmail}");
+            lines = lines.Replace("{{ownerApp}}", $"{workflowDefinition.OwnerApp}");
+            lines = lines.Replace("{{originalName}}", $"{workflowDefinition.Name}");
+            lines = lines.Replace("{{description}}", workflowDescription);
+            lines = lines.Replace("{{commentDescription}}", workflowDescription?.Replace('\n', ','));
+
+            if (_config.Dryrun)
+                return null;
+
+            return lines;
+        }
+
+        private void AppendClassProperty(StringBuilder linesBuilder, string type, string name)
+        {
+            linesBuilder.AppendLine($"        [JsonProperty(\"{name}\")]");
+            LangUtils.MakeValidMemberName(SnakeCaseUtil.ToPascalCase(name), "A", out var propertyName);
+            linesBuilder.AppendLine($"        public {type} {propertyName} {{ get; set; }}");
+        }
+
+        private void AppendXmlComment(StringBuilder linesBuilder, string tag, string value)
+        {
+            linesBuilder.AppendLine($"        /// <{tag}>");
+            linesBuilder.AppendLine($"        /// {value}");
+            linesBuilder.AppendLine($"        /// </{tag}>");
+        }
+
+        public string CreateTaskClass(TaskDefinition taskDefinition)
+        {
+            var lines = EmbeddedFileHelper.GetLinesFromEmbeddedFile("~/Templates/WorkerTemplate.default");
+
+            var namespaceRegex = new Regex("(^[^_]+)_.+");
+
+            var matches = namespaceRegex.Match(taskDefinition.Name).Groups;
+
+            string naspace = "";
+            if (matches.Count > 1)
+            {
+                naspace = matches[1].Value.ToLowerInvariant();
+                naspace = char.ToUpperInvariant(naspace[0]) + naspace[1..];
+            }
+
+            string name = SnakeCaseUtil.ToPascalCase(taskDefinition.Name).Trim();
+            string note = null;
+            if (LangUtils.MakeValidMemberName(name, "A", out name))
+            {
+                note = "The autogenerated name has been prepended because it starts with a digit character.";
+            }
+
+            var inputLinesBuilder = new StringBuilder();
+            foreach (var property in taskDefinition.InputKeys)
+            {
+                AppendXmlComment(inputLinesBuilder, "originalName", property);
+                AppendClassProperty(inputLinesBuilder, "dynamic", property);
+            }
+
+            var outputLinesBuilder = new StringBuilder();
+            foreach (var property in taskDefinition.OutputKeys)
+            {
+                AppendXmlComment(outputLinesBuilder, "originalName", property);
+                AppendClassProperty(outputLinesBuilder, "dynamic", property);
+            }
+
+            var generatedNamespace = _config.BaseNamespace + ".Tasks" + (!string.IsNullOrEmpty(naspace) ? $".{naspace}" : "");
+
+            lines = lines.Replace("{{workerName}}", name);
+            lines = lines.Replace("{{note}}", note);
+            lines = lines.Replace("{{inputProperties}}", inputLinesBuilder.ToString());
+            lines = lines.Replace("{{outputProperties}}", outputLinesBuilder.ToString());
+            lines = lines.Replace("{{namespace}}", $"{generatedNamespace}");
+            lines = lines.Replace("{{ownerEmail}}", $"{taskDefinition.OwnerEmail}");
+            lines = lines.Replace("{{ownerApp}}", $"{taskDefinition.OwnerApp}");
+            lines = lines.Replace("{{originalName}}", $"{taskDefinition.Name}");
+
+            var description = "";
+            try
+            {
+                description = JObject.Parse(taskDefinition.Description).SelectToken("description").Value<string>();
+            }
+            catch (Exception)
+            {
+                _logger.LogWarning($"Invalid description '{taskDefinition.Description}' in task {taskDefinition.Name}");
+            }
+
+            var ownerEmail = taskDefinition.OwnerEmail;
+            var ownerApp = taskDefinition.OwnerApp;
+
+            if (string.IsNullOrEmpty(ownerEmail))
+                _logger.LogWarning($"No owner email defined for task {taskDefinition.Name}");
+
+            if (string.IsNullOrEmpty(ownerApp))
+                _logger.LogWarning($"No owner app defined for task {taskDefinition.Name}");
+
+            lines = lines.Replace("{{description}}", description);
+            lines = lines.Replace("{{commentDescription}}", description.Replace('\n', ','));
+
+            if (_config.Dryrun)
+                return null;
+
+            return lines;
+        }
+    }
+}
diff --git a/src/ConductorSharp.Toolkit/Templates/TaskCollectionTemplate.default b/src/ConductorSharp.Toolkit/Templates/TaskCollectionTemplate.default
new file mode 100644
index 00000000..38ae9b09
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/Templates/TaskCollectionTemplate.default
@@ -0,0 +1,20 @@
+﻿using ConductorSharp.Engine.Model;
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using Newtonsoft.Json;
+using MediatR;
+using ConductorSharp.Engine.Util;
+
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace {{namespace}}
+{
+    {{taskCollection}}
+}
diff --git a/src/ConductorSharp.Toolkit/Templates/WorkerTemplate.default b/src/ConductorSharp.Toolkit/Templates/WorkerTemplate.default
new file mode 100644
index 00000000..bd834386
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/Templates/WorkerTemplate.default
@@ -0,0 +1,31 @@
+﻿    public partial class {{workerName}}Input : IRequest<{{workerName}}Output>
+    {
+{{inputProperties}}
+    }
+
+    public partial class {{workerName}}Output
+    {
+{{outputProperties}}
+    }
+
+    /// <summary>
+    /// {{commentDescription}}
+    /// </summary>
+    /// <originalName>
+    /// {{originalName}}
+    /// </originalName>
+    /// <ownerEmail>
+    /// {{ownerEmail}}
+    /// </ownerEmail>
+    /// <ownerApp>
+    /// {{ownerApp}}
+    /// </ownerApp>
+    /// <note>
+    /// {{note}}
+    /// </note>
+    [OriginalName("{{originalName}}")]
+    public partial class {{workerName}} : SimpleTaskModel<{{workerName}}Input, {{workerName}}Output>
+    {
+    }
+
+
diff --git a/src/ConductorSharp.Toolkit/Templates/WorkflowCollectionTemplate.default b/src/ConductorSharp.Toolkit/Templates/WorkflowCollectionTemplate.default
new file mode 100644
index 00000000..98daf1a7
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/Templates/WorkflowCollectionTemplate.default
@@ -0,0 +1,21 @@
+﻿using ConductorSharp.Engine.Model;
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using Newtonsoft.Json;
+using ConductorSharp.Engine.Builders;
+using MediatR;
+using ConductorSharp.Engine.Util;
+
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace {{namespace}}
+{
+    {{workflowCollection}}
+}
diff --git a/src/ConductorSharp.Toolkit/Templates/WorkflowTemplate.default b/src/ConductorSharp.Toolkit/Templates/WorkflowTemplate.default
new file mode 100644
index 00000000..684cccd7
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/Templates/WorkflowTemplate.default
@@ -0,0 +1,30 @@
+﻿    public partial class {{workflowName}}Input : IRequest<{{workflowName}}Output>
+    {
+{{inputProperties}}
+    }
+
+    public partial class {{workflowName}}Output
+    {
+    }
+
+    /// <summary>
+    /// {{commentDescription}}
+    /// </summary>
+    /// <originalName>
+    /// {{originalName}}
+    /// </originalName>
+    /// <ownerEmail>
+    /// {{ownerEmail}}
+    /// </ownerEmail>
+    /// <ownerApp>
+    /// {{ownerApp}}
+    /// </ownerApp>
+    /// <note>
+    /// {{note}}
+    /// </note>
+    [OriginalName("{{originalName}}")]
+    public partial class {{workflowName}} : SubWorkflowTaskModel<{{workflowName}}Input, {{workflowName}}Output>
+    {
+    }
+
+
diff --git a/src/ConductorSharp.Toolkit/ToolkitModule.cs b/src/ConductorSharp.Toolkit/ToolkitModule.cs
new file mode 100644
index 00000000..60e67104
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/ToolkitModule.cs
@@ -0,0 +1,19 @@
+﻿using Autofac;
+using ConductorSharp.Client.Service;
+using ConductorSharp.Toolkit.Commands;
+using ConductorSharp.Toolkit.Service;
+
+namespace ConductorSharp.Toolkit
+{
+    public class ToolkitModule : Module
+    {
+        protected override void Load(ContainerBuilder builder)
+        {
+            builder.RegisterType<ConductorClient>().As<IConductorClient>();
+            builder.RegisterType<MetadataService>().As<IMetadataService>();
+            builder.RegisterType<ScaffoldingService>().As<IScaffoldingService>();
+            builder.RegisterType<CommandRegistry>();
+            builder.RegisterType<ScaffoldCommand>().As<Command>();
+        }
+    }
+}
diff --git a/src/ConductorSharp.Toolkit/Util/EmbeddedFileHelper.cs b/src/ConductorSharp.Toolkit/Util/EmbeddedFileHelper.cs
new file mode 100644
index 00000000..47b3f9a9
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/Util/EmbeddedFileHelper.cs
@@ -0,0 +1,52 @@
+﻿using Newtonsoft.Json;
+using System.Reflection;
+using System.Text;
+
+namespace ConductorSharp.Toolkit.Util
+{
+    internal static class EmbeddedFileHelper
+    {
+        private static string ReadAssemblyFile(Assembly assembly, string name)
+        {
+            var stream = assembly.GetManifestResourceStream(name);
+
+            if (stream == null)
+                throw new InvalidOperationException($"Resource {name} does not exist.");
+
+            using var reader = new StreamReader(stream, Encoding.UTF8);
+
+            return reader.ReadToEnd();
+        }
+
+        public static T GetObjectFromEmbeddedFile<T>(string fileName, params (string Key, object Value)[] templateParams)
+        {
+            fileName = fileName.Replace("~/", typeof(EmbeddedFileHelper).Assembly.GetName().Name + ".").Replace("/", ".");
+
+            var contents = ReadAssemblyFile(typeof(EmbeddedFileHelper).Assembly, fileName);
+
+            if (templateParams != null)
+                foreach (var (Key, Value) in templateParams)
+                    contents = contents.Replace("{{" + Key + "}}", $"{Value}");
+
+            return JsonConvert.DeserializeObject<T>(contents);
+        }
+
+        public static string GetLinesFromEmbeddedFile(string fileName)
+        {
+            fileName = fileName.Replace("~/", typeof(EmbeddedFileHelper).Assembly.GetName().Name + ".").Replace("/", ".");
+
+            var contents = ReadAssemblyFile(typeof(EmbeddedFileHelper).Assembly, fileName);
+
+            return contents;
+        }
+
+        public static Task<T> GetObjectFromEmbeddedFileAsync<T>(string fileName, params (string Key, object Value)[] templateParams) =>
+            Task.FromResult(GetObjectFromEmbeddedFile<T>(fileName, templateParams));
+
+        public static string Reserialize<T>(string fileName, params (string Key, object Value)[] templateParams)
+        {
+            var file = GetObjectFromEmbeddedFile<T>(fileName, templateParams);
+            return JsonConvert.SerializeObject(file);
+        }
+    }
+}
diff --git a/src/ConductorSharp.Toolkit/Util/LangUtils.cs b/src/ConductorSharp.Toolkit/Util/LangUtils.cs
new file mode 100644
index 00000000..2807c5ec
--- /dev/null
+++ b/src/ConductorSharp.Toolkit/Util/LangUtils.cs
@@ -0,0 +1,34 @@
+﻿using System.Text.RegularExpressions;
+
+namespace ConductorSharp.Toolkit.Util
+{
+    internal static class LangUtils
+    {
+
+        // There are more characters, so far this is enough
+        private const string InvalidCharactersRegex = @" |@|,|:|\||;|\||\(|\)|\[|\]|\.";
+
+        public static bool MakeValidMemberName(string memberName, string prefix, out string validMemberName)
+        {
+            bool changedMemberName = false;
+            validMemberName = memberName;
+
+            if (char.IsDigit(memberName[0]))
+            {
+                validMemberName = $"{prefix}{memberName}";
+                changedMemberName = true;
+            }
+
+            if (RemoveInvalidCharacters(validMemberName, out validMemberName))
+                changedMemberName = true;
+
+            return changedMemberName;
+        }
+
+        private static bool RemoveInvalidCharacters(string memberName, out string validMemberName)
+        {
+            validMemberName = Regex.Replace(memberName, InvalidCharactersRegex, string.Empty);
+            return memberName != validMemberName;
+        }
+    }
+}
