diff --git a/src/Microsoft.AspNet.Razor/NotNullArgument.cs b/src/Microsoft.AspNet.Razor/NotNullArgument.cs
new file mode 100644
index 0000000000..43016aeb51
--- /dev/null
+++ b/src/Microsoft.AspNet.Razor/NotNullArgument.cs
@@ -0,0 +1,12 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+namespace Microsoft.AspNet.Razor
+{
+ [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
+ internal sealed class NotNullAttribute : Attribute
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/ContentBehavior.cs b/src/Microsoft.AspNet.Razor/TagHelpers/ContentBehavior.cs
new file mode 100644
index 0000000000..c513d5a710
--- /dev/null
+++ b/src/Microsoft.AspNet.Razor/TagHelpers/ContentBehavior.cs
@@ -0,0 +1,46 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.AspNet.Razor.TagHelpers
+{
+ ///
+ /// Defines how a tag helper will utilize its inner HTML.
+ ///
+ public enum ContentBehavior
+ {
+ ///
+ /// Indicates that the tag helper will not modify its inner HTML in any way. This is the default
+ /// .
+ ///
+ /// Children of the current tag helper will execute after the current tag helper.
+ None,
+
+ ///
+ /// Indicates that the tag helper wants anything within its tag builder's inner HTML to be
+ /// appended to content its children generate.
+ ///
+ /// Children of the current tag helper will execute before the current tag helper.
+ Append,
+
+ ///
+ /// Indicates that the tag helper will modify its HTML content. Therefore this
+ /// enables the tag helper to examine the content its children generate.
+ ///
+ /// Children of the current tag helper will execute before the current tag helper.
+ Modify,
+
+ ///
+ /// Indicates that the tag helper wants anything within its tag builder's inner HTML to be
+ /// prepended to the content its children generate.
+ ///
+ /// Children of the current tag helper will execute after the current tag helper.
+ Prepend,
+
+ ///
+ /// Indicates that the tag helper wants anything within its tag builder's inner HTML to
+ /// replace any HTML inside of it.
+ ///
+ /// Children of the current tag helper will not execute.
+ Replace,
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperAttributeDescriptor.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperAttributeDescriptor.cs
new file mode 100644
index 0000000000..17529bc8ae
--- /dev/null
+++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperAttributeDescriptor.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Reflection;
+
+namespace Microsoft.AspNet.Razor.TagHelpers
+{
+ ///
+ /// A metadata class describing a tag helper attribute.
+ ///
+ public class TagHelperAttributeDescriptor
+ {
+ ///
+ /// Instantiates a new class.
+ ///
+ /// The HTML attribute name.
+ /// The for the tag
+ /// helper attribute
+ public TagHelperAttributeDescriptor(string attributeName,
+ PropertyInfo propertyInfo)
+ {
+ AttributeName = attributeName;
+ PropertyInfo = propertyInfo;
+ }
+
+ ///
+ /// The HTML attribute name.
+ ///
+ public string AttributeName { get; private set; }
+
+ ///
+ /// The name of the CLR property name that corresponds to the HTML attribute name.
+ ///
+ public string AttributePropertyName
+ {
+ get
+ {
+ return PropertyInfo.Name;
+ }
+ }
+
+ ///
+ /// The for the tag helper attribute
+ ///
+ public PropertyInfo PropertyInfo { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs
new file mode 100644
index 0000000000..01a25ac9b8
--- /dev/null
+++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs
@@ -0,0 +1,53 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using Microsoft.Internal.Web.Utils;
+
+namespace Microsoft.AspNet.Razor.TagHelpers
+{
+ ///
+ /// A metadata class describing a tag helper.
+ ///
+ public class TagHelperDescriptor
+ {
+ ///
+ /// Instantiates a new instance of the class.
+ ///
+ /// The tag name that the tag helper targets. '*' indicates a catch-all
+ /// which applies to every HTML tag.
+ /// The full name of the tag helper class.
+ /// The
+ /// of the tag helper.
+ public TagHelperDescriptor(string tagName,
+ string tagHelperName,
+ ContentBehavior contentBehavior)
+ {
+ TagName = tagName;
+ TagHelperName = tagHelperName;
+ ContentBehavior = contentBehavior;
+ Attributes = new List();
+ }
+
+ ///
+ /// The tag name that the tag helper should target.
+ ///
+ public string TagName { get; private set; }
+
+ ///
+ /// The full name of the tag helper class.
+ ///
+ public string TagHelperName { get; private set; }
+
+ ///
+ /// The of the tag helper.
+ ///
+ public ContentBehavior ContentBehavior { get; private set; }
+
+ ///
+ /// The list of attributes the tag helper expects.
+ ///
+ public virtual List Attributes { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorProvider.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorProvider.cs
new file mode 100644
index 0000000000..f7e7bfc690
--- /dev/null
+++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorProvider.cs
@@ -0,0 +1,107 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Internal.Web.Utils;
+
+namespace Microsoft.AspNet.Razor.TagHelpers
+{
+ ///
+ /// Enables retrieval of 's.
+ ///
+ public class TagHelperDescriptorProvider
+ {
+ private const string CatchAllDescriptorTarget = "*";
+
+ private static readonly TagHelperDescriptorComparer DefaultTagHelperDescriptorComparer =
+ new TagHelperDescriptorComparer();
+
+ private IDictionary> _registrations;
+
+ ///
+ /// Instantiates a new instance of the .
+ ///
+ /// The descriptors that the will pull from.
+ public TagHelperDescriptorProvider(IEnumerable descriptors)
+ {
+ _registrations = new Dictionary>(StringComparer.OrdinalIgnoreCase);
+
+ // Populate our registrations
+ foreach (var descriptor in descriptors)
+ {
+ Register(descriptor);
+ }
+ }
+
+ ///
+ /// Gets all tag helpers that match the given .
+ ///
+ /// The name of the HTML tag to match. Providing a '*' tag name
+ /// retrieves catch-all s (descriptors that target every tag).
+ /// s that apply to the given .
+ /// Will return an empty if no s are
+ /// found.
+ public IEnumerable GetTagHelpers(string tagName)
+ {
+ HashSet descriptors;
+
+ // Ensure there's an ISet to use.
+ if (!_registrations.TryGetValue(CatchAllDescriptorTarget, out descriptors))
+ {
+ descriptors = new HashSet();
+ }
+
+ // If the requested tag name is the catch-all target, we should short circuit.
+ if (tagName.Equals(CatchAllDescriptorTarget, StringComparison.OrdinalIgnoreCase))
+ {
+ return descriptors;
+ }
+
+ // If we have a tag name associated with the requested name, return the descriptors +
+ // all of the catch-all descriptors.
+ if (_registrations.TryGetValue(tagName, out var matchingDescriptors))
+ {
+ return matchingDescriptors.Concat(descriptors);
+ }
+
+ // We couldn't any descriptors associated with the requested tag name, return all
+ // of the "catch-all" tag descriptors (there may not be any).
+ return descriptors;
+ }
+
+ private void Register(TagHelperDescriptor descriptor)
+ {
+ HashSet descriptorSet;
+
+ // Ensure there's a List to add the descriptor to.
+ if (!_registrations.TryGetValue(descriptor.TagName, out descriptorSet))
+ {
+ descriptorSet = new HashSet(DefaultTagHelperDescriptorComparer);
+ _registrations[descriptor.TagName] = descriptorSet;
+ }
+
+ descriptorSet.Add(descriptor);
+ }
+
+ private class TagHelperDescriptorComparer : IEqualityComparer
+ {
+ public bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
+ {
+ return descriptorX.TagHelperName == descriptorY.TagHelperName &&
+ descriptorX.TagName == descriptorY.TagName &&
+ descriptorX.ContentBehavior == descriptorY.ContentBehavior;
+ }
+
+ public int GetHashCode(TagHelperDescriptor descriptor)
+ {
+ return HashCodeCombiner.Start()
+ .Add(descriptor.TagName)
+ .Add(descriptor.TagHelperName)
+ .Add(descriptor.ContentBehavior)
+ .CombinedHash;
+ }
+ }
+ }
+}
\ No newline at end of file