diff --git a/core/src/main/kotlin/net/corda/core/node/CordaPluginRegistry.kt b/core/src/main/kotlin/net/corda/core/node/CordaPluginRegistry.kt
index 3410aed39b..c0e227c9be 100644
--- a/core/src/main/kotlin/net/corda/core/node/CordaPluginRegistry.kt
+++ b/core/src/main/kotlin/net/corda/core/node/CordaPluginRegistry.kt
@@ -1,5 +1,7 @@
 package net.corda.core.node
 
+import com.esotericsoftware.kryo.Kryo
+
 /**
  * Implement this interface on a class advertised in a META-INF/services/net.corda.core.node.CordaPluginRegistry file
  * to extend a Corda node with additional application services.
@@ -35,4 +37,16 @@ abstract class CordaPluginRegistry {
      * allow access to the protocol factory and protocol initiation entry points there.
      */
     open val servicePlugins: List<Class<*>> = emptyList()
+
+    /**
+     * Optionally register types with [Kryo] for use over RPC, as we lock down the types that can be serialised in this
+     * particular use case.
+     * For example, if you add an RPC interface that carries some contract states back and forth, you need to register
+     * those classes here using the [register] method on Kryo.
+     *
+     * TODO: Kryo and likely the requirement to register classes here will go away when we replace the serialization implementation.
+     *
+     * @return true if you register types, otherwise you will be filtered out of the list of plugins considered in future.
+     */
+    open fun registerRPCKryoTypes(kryo: Kryo): Boolean = false
 }
diff --git a/docs/source/clientrpc.rst b/docs/source/clientrpc.rst
index e918b116d4..0cb10d1077 100644
--- a/docs/source/clientrpc.rst
+++ b/docs/source/clientrpc.rst
@@ -80,5 +80,17 @@ Wire protocol
 The client RPC wire protocol is not currently documented. To use it you must use the client library provided.
 This is likely to change in a future release.
 
+Registering Classes With RPC Kryo
+---------------------------------
+
+In the present implementation of the node we use Kryo to generate the *on the wire* representation of contracts states
+or any other classes that form part of the RPC arguments or response.  To avoid the RPC interface being wide open to all
+classes on the classpath, Cordapps will currently have to register any classes or custom serialisers they require with Kryo
+if they are not one of those registered by default in ``RPCKryo`` via the plugin architecture.  See :doc:`creating-a-cordapp`.
+This will require some familiarity with Kryo.  An example is shown in :doc:`tutorial-clientrpc-api`.
+
+.. warning:: We will be replacing the use of Kryo in RPC with a stable message format and this will mean that this plugin
+   customisation point will either go away completely or change.
+
 .. _CordaRPCClient: api/net.corda.client/-corda-r-p-c-client/index.html
 .. _CordaRPCOps: api/net.corda.node.services.messaging/-corda-r-p-c-ops/index.html
diff --git a/docs/source/creating-a-cordapp.rst b/docs/source/creating-a-cordapp.rst
index b05d76e3d1..bca9ddfa1b 100644
--- a/docs/source/creating-a-cordapp.rst
+++ b/docs/source/creating-a-cordapp.rst
@@ -16,6 +16,7 @@ specific details of the implementation, but you can extend the server in the fol
 2. Service plugins: Register your services (see below).
 3. Web APIs: You may register your own endpoints under /api/ of the built-in web server.
 4. Static web endpoints: You may register your own static serving directories for serving web content.
+5. Registering your additional classes used in RPC.
 
 Services
 --------
diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt
index 871bd45924..364d14693f 100644
--- a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt
+++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt
@@ -1,5 +1,6 @@
 package net.corda.docs
 
+import com.esotericsoftware.kryo.Kryo
 import net.corda.client.CordaRPCClient
 import net.corda.contracts.asset.Cash
 import net.corda.core.contracts.Amount
@@ -7,6 +8,7 @@ import net.corda.core.contracts.Issued
 import net.corda.core.contracts.PartyAndReference
 import net.corda.core.contracts.USD
 import net.corda.core.div
+import net.corda.core.node.CordaPluginRegistry
 import net.corda.core.node.services.ServiceInfo
 import net.corda.core.serialization.OpaqueBytes
 import net.corda.core.transactions.SignedTransaction
@@ -135,4 +137,17 @@ fun generateTransactions(proxy: CordaRPCOps) {
         }
     }
 }
-// END 6
\ No newline at end of file
+// END 6
+
+// START 7
+data class ExampleRPCValue(val foo: String)
+
+class ExampleRPCCordaPluginRegistry : CordaPluginRegistry() {
+    override fun registerRPCKryoTypes(kryo: Kryo): Boolean {
+        // Add classes like this.
+        kryo.register(ExampleRPCValue::class.java)
+        // You should return true, otherwise your plugin will be ignored for registering classes with Kryo.
+        return true
+    }
+}
+// END 7
diff --git a/docs/source/tutorial-clientrpc-api.rst b/docs/source/tutorial-clientrpc-api.rst
index c9419f716b..89abcf45a1 100644
--- a/docs/source/tutorial-clientrpc-api.rst
+++ b/docs/source/tutorial-clientrpc-api.rst
@@ -79,3 +79,19 @@ Now let's try to visualise the transaction graph. We will use a graph drawing li
     :end-before: END 5
 
 If we run the client with ``Visualise`` we should see a simple random graph being drawn as new transactions are being created.
+
+Registering classes from your Cordapp with RPC Kryo
+--------------------------------------------------
+
+As described in :doc:`clientrpc`, you currently have to register any additional classes you add that are needed in RPC
+requests or responses with the `Kryo` instance RPC uses.  Here's an example of how you do this for an example class.
+
+.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt
+    :language: kotlin
+    :start-after: START 7
+    :end-before: END 7
+
+See more on plugins in :doc:`creating-a-cordapp`.
+
+.. warning:: We will be replacing the use of Kryo in RPC with a stable message format and this will mean that this plugin
+    customisation point will either go away completely or change.
\ No newline at end of file
diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/RPCStructures.kt b/node/src/main/kotlin/net/corda/node/services/messaging/RPCStructures.kt
index 053459f1f8..beff338c18 100644
--- a/node/src/main/kotlin/net/corda/node/services/messaging/RPCStructures.kt
+++ b/node/src/main/kotlin/net/corda/node/services/messaging/RPCStructures.kt
@@ -20,10 +20,7 @@ import net.corda.core.crypto.DigitalSignature
 import net.corda.core.crypto.Party
 import net.corda.core.crypto.PublicKeyTree
 import net.corda.core.crypto.SecureHash
-import net.corda.core.node.NodeInfo
-import net.corda.core.node.PhysicalLocation
-import net.corda.core.node.ServiceEntry
-import net.corda.core.node.WorldCoordinate
+import net.corda.core.node.*
 import net.corda.core.node.services.*
 import net.corda.core.protocols.StateMachineRunId
 import net.corda.core.serialization.*
@@ -117,6 +114,14 @@ class PermissionException(msg: String) : RuntimeException(msg)
 // This is annoying to write out, but will make it easier to formalise the wire protocol when the time comes,
 // because we can see everything we're using in one place.
 private class RPCKryo(observableSerializer: Serializer<Observable<Any>>? = null) : Kryo() {
+    companion object {
+        private val pluginRegistries: List<CordaPluginRegistry> by lazy {
+            val unusedKryo = Kryo()
+            // Sorting required to give a stable ordering, as Kryo allocates integer tokens for each registered class.
+            ServiceLoader.load(CordaPluginRegistry::class.java).toList().filter { it.registerRPCKryoTypes(unusedKryo) }.sortedBy { it.javaClass.name }
+        }
+    }
+
     init {
         isRegistrationRequired = true
         // Allow construction of objects using a JVM backdoor that skips invoking the constructors, if there is no
@@ -215,6 +220,7 @@ private class RPCKryo(observableSerializer: Serializer<Observable<Any>>? = null)
         register(ProtocolHandle::class.java)
         register(KryoException::class.java)
         register(StringBuffer::class.java)
+        pluginRegistries.forEach { it.registerRPCKryoTypes(this) }
     }
 
     // Helper method, attempt to reduce boiler plate code