We propose MCP 2.0 as a low-bandwidth MOO server-client out-of-band protocol which extends and revises MCP 1.0 as described by Ostrom, Van Buren, Curtis, Nichols, and Carlson in MOO Client Request Protocol. We welcome your comments on this proposal. Please note this is still a working document; in particular, we are also trying to coordinate our efforts with others working in this area before posting a formal RFC to moo-cows.
First, we give a formal grammar (in extended Backus-Naur form), and then we give several examples of the protocol in use. The grammar is the same for transmissions from the server to the client as for those from the client to the server. We then describe our motivations for defining this new protocol. Finally, we also describe the other commands which constitute the rest of the Client feature in the Nova protocol.
; Preliminaries space := ' ' letter := 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' digit := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' quote := '"' backslash := '\' symbol := '-' | '!' | '@' | ... ; symbols are any printing 7-bit ASCII characters other than quote, backslash, ; letter, or digit (this also excludes tab and space, which are non-printing). word := { letter | digit }* symbolWord := { letter | digit | symbol | backslash }* any := letter | digit | symbol | space | tab | backslash backslash | backslash quote quotedWord := quote [any*] quote ; Notice that quotedWord includes possibly the empty string "" ; ; The Main Grammar ; OOBcommand := OOBprefix action [data*] ; This represents the default value, but it may be changed by client or server OOBprefix := '#' '$' '#' action := symbolWord data := space* keyword [space*] '=' [space*] value keyword := word value := symbolWord | quotedWord
The following are examples of valid MCP2.0 strings
#$#audio-play name=gong volume="loud" #$#foo empyString="" #$#foo singleQuote="\"" #$#foo space=" " backslash="\\" anotherBackslash=\ #$#foo notice=valueMayContain=Or\WithoutQuoting
The following are examples of invalid MCP2.0 strings
#$# noSpaceShouldSeparateActionAndOOBPrefix #$#actionMustNotContain"=\orSpaceOrTab #$#action-may-contain-hyphens_underscores_and-other-symbols #$#foo requestMayNotContain"=\SpaceOrTabEither #$#foo bar oops=BarIsMissingAnAssociatedValue #$#foo bar=valueMayNotContain"OrSpaceOrTab_UNLESS_ItIsQuoted
We require a standardized protocol for communicating between server and client as a part of our Supernova project. MCP2.0 represents our attempt to regularize MCP1.0, with minor changes to the syntax and with the removal of any assumptions on the interpretation of MCP lines of text.
Please note that MCP is only a substrate -- a format for OOB commands together with a mechanism for client and server to agree upon the protocol they will speak. By itself, MCP is not useful; clients and servers will work out specialized protocols (which we hope will be built upon and thus compatible with MCP2.0) to provide the functionality they require. For example, our Supernova client will speak a "dialect" of MCP2.0, called Nova. In this way a single parser may be able to decode any MCP2.0 compliant OOB command received, but processing that command will depend on the particular protocol (Jupiter, Nova, etc.) currently installed.
We expect a typical client to speak only a single protocol, while a server might speak many. This expectation is already borne out in existing MOOs (which have mechanisms for Jupiter, Pueblo, MacMOOSE, ANSI escape codes, and so forth) and in other client/server models as well. Furthermore, since the user initiates the connection to the server, it should be the user's choice (when a choice exists) as to which protocol to use.
In our experience, actions are most conveniently expressed as
action := feature '-' request feature := word request := symbolWordthough we do not require this.
Each feature can then be easily mapped to a feature handler (in the server and in the client) which dispatches requests corresponding to that feature. So, for example, an Audio Feature might intercept any OOB commands with the feature = "audio". We do not wish to legislate this convention, but we will adhere to it in what follows.
Clearly the protocol must also include a mechanism by which
Consequently, we require any MCP compliant server or client to support a single feature, the "client" feature. This feature is responsible for the functionality just described, though it might also handle other activities (such as client upgrades or setting or getting client preferences) as well.
Client protocol negotiation (CPN) is the process by which client and server agree on a mutual OOB protocol. This process is client-driven and initiated at the earliest possible moment.
This process is client-driven for backwards compatibility with clients that do not support MCP (such as raw telnet, which would display any OOB commands received as a cryptic message to the user, or Web browsers, which can become confused by OOB commands). Therefore we require that the server will not send any OOB commands to the client until the client has first sent an OOB command to the server. Although CPN may occur at any time during the login process (allowing a client to switch to a new protocol, if desired), this process is begun as early as possible upon connection, even prior to logging in, in case the server wishes to customize the login process (for example, popping up a window asking for various bits of user information). This complicates the OOB handling on the server side, but we believe it provides the greatest flexibility.
This first OOB command identifies the client, requests a protocol, and may specify other optional information as well (indicating, for example, an encryption scheme to be used for future communications). Notice, however, that additional data sent will probably not be understood by the server unless it speaks the given protocol. This command thus takes the form
#$#client-protocol name="whatever the protocol name is" version="and its version" [possibly other key/value pairs]
This command indicates a desire on the part of the client to speak the specified OOB protocol with the server. The server is expected to respond, either negatively (definitively refusing the suggested protocol), positively (accepting the suggested protocol), or ambivalently (not replying at all, or possibly suggesting another protocol instead). The client then responds similarly to the server's response. This process continues until the client either decides on a protocol, or else gives up trying. The ultimate decision lies with the client; unless the server receives a protocol-ok message from the client, it must assume that no OOB protocol has been agreed upon.
We do not specify the format of ambivalent responses. If, for example, the server has not responded quickly enough, the client may try its suggestion again; there is no harm in this. The server might also respond with an OOB message indicating that the server is full and ask the client to wait a few minutes. Or it might tell the client the location of the latest version of the client, in case it can upgrade itself. We leave these details to whatever specific dialect of MCP the two speak.
Protocol suggestions we have already seen. For responding definitively yes or no to a protocol, the messages are
#$#client-protocol-ok name="whatever the protocol name is" version="and its version" [possibly other key/value pairs] #$#client-protocol-no name="whatever the protocol name is" version="and its version" [possibly other key/value pairs]
respectively. It is not necessary to definitively veto a protocol; suggesting an alternate protocol with an #$#client-protocol command implies this.
Again, the client protocol negotiation process continues until the server receives a #$#client-protocol-ok from the client. Thus if the server has OK'd a suggestion from the client, the client must confirm this selection to the server. Therefore, a typical client protocol negotiation looks like this:
->#$#client-protocol name=nova version=1.0 <-#$#client-protocol-ok name=nova version=1.0 ->#$#client-protocol-ok name=nova version=1.0(-> indicates from client to server, <- indicates from server to client)
Secondly, we require a mechanism by which each participant may learn about the feature set of the other. This is done after client protocol negotiation has concluded, and therefore is up to the particular protocol being spoken. That is, client feature negotiation is not a part of MCP2.0 (which specifies only a format for OOB messages and a CPN process), but rather a part of the specification of whatever protocol was agreed upon.
With this in mind, we include our mechanism for client feature negotiation in the Nova protocol spoken by the client Supernova; other protocols may use or not use this method as they wish.
These are the commands involved in CFN:
#$#client-features-query #$#client-features featureList="space-delimited feature list" #$#client-feature-query feature="feature name" #$#client-feature-info feature="feature name" version="version name" [possibly other data describing the feature] #$#client-feature-upgrade feature="feature name" url="location of upgrade"
The first of these, #$#client-features-query, is typically sent by the server to the client, in order to determine which features the client supports. It may also be sent the other way, for the same reason. The response to a client feature query is #$#client-features, listing all the available features.
We also provides three less often used commands, for obtaining additional information or (possibly) upgrading outdated features. If the requester needs additional information about some or all of these features, that information can be obtained by sending a #$#client-feature-query for that specific feature. The response to this command is #$#client-feature-info, describing the requested feature. Finally, #$#client-feature-upgrade can be used to suggest an upgrade.
Additionally, we provide two commands for modifying the list of currently available features. #$#client-feature-add is used to indicate that new functionality has been acquired, while #$#client-feature-remove is used to indicate that a feature is no longer available (this may be a temporary condition, in which case the feature can be added back later).
Another feature which is useful in a protocol is the ability to put several lines of data into a single value. Our solution for this in the Nova protocol is the following:
NOTE: This section has changed to adopt a modified version of the method used by Dave Kormann and Erik Ostrom in their working draft of MCP2.1. Thanks for the suggestions!
Keywords ending in * are interpreted to have a special meaning, namely that the keyword/value data pair is tagged. The value assigned to the keyword is the tag. Tagged data is not dispatched as normal, but is enqueued pending its arrival sometime in the future. This mechanism works thusly:
#$#editor-append text*=foo note="The value of \"text\" has not yet been sent." #$#client-tag foo=Everything to end of line corresponds to the next line of data. #$#client-tag foo=Additional lines are added to the value of the data. #$#client-tag foo=For efficiency, these lines are not required to be escaped or quoted #$#client-tag foo=as they normally would be. Instead, everything to the end of each #$#client-tag foo=line corresponds to an entire line of text. #$#client-tag foo=The next message indicates the end of this multiline (tagged) data. #$#client-tag-end tag=foo
Only after all the tagged data in a command has been fully received is the command dispatched. Note that the tag must satisfy the conditions for keyword names (that is, be a word), and must be kept unique for its lifetime. Because of latency issues, we recommend that it be kept unique for the duration of the connection.
Messages from server to client:
#$#client-redirect [host=hostIPaddressOrHostName] port=portNumber [possibly other data]
Examples:
#$#client-redirect host=astrovr.ipac.caltech.edu port=8888 #$#client-redirect host=127.0.0.1 port=1234 #$#client-redirect port=7777
#$#client-redirect-ok [possibly other data]
Examples:
#$#client-redirect-ok
#$#client-redirect-no [possibly other data]
Examples:
#$#client-redirect-no reason="Server is full" estimatedWaitTime="6 minutes"
Messages from client to server:
#$#client-redirect host=hostWhichIsRedirecting port=itsPortNumber [possibly other data]
Examples:
#$#client-redirect host=astrovr.ipac.caltech.edu port=8888
#$#client-redirect-failed [possibly other data]
Examples:
#$#client-redirect-failed reason="Server is full" estimatedWaitTime="6 minutes"
Note: When the host information is omitted, its value defaults to that of the current connection.
When the client receives the client-redirect command from the server, it will ignore all additional information sent from the current connection (with some exceptions described below), pending successful redirection to the new network location. Upon successful connection to the new site/port, the client sends a client-redirect command of its own, indicating the site and port from which it was redirected. It also passes any additional data that was sent with the original client-redirect command from the redirecting server -- in this way, servers may pass information between one another through the client. [Because this happens invisibly to the user, it allows servers to act as parasites of sorts on the clients -- so it's important to provide an option to the user to disallow client-redirection, or else to signal the user that it's happening. However, this can also interfere with the VR feel (immersion) in a truly distributed MOO which passes users from one site to another as they travel from room to room. So make it a preference, and let the user choose.]
The new server responds either with client-redirect-ok or client-redirect-no, and again additional data (such as the reason for failure) may be passed, this time back to the original server. If the server responded with ok, then the redirection is considered to have succeeded, and the client drops the former connection in favor of the new one (after passing the additional data, if any, to the original server). Any data (in-band or out-of-band) that was sent from either server between the initiating client-redirect request and the final client-redirect-ok may is to be ignored. If, on the other hand, the redirect request is refused with client-redirect-no, then the new connection is dropped (also ignoring any other input from it) and the original connection is maintained. Any data (in-band or out-of-band) that was received from the original connection during this time may now be processed normally.
Not yet written.
The client and server may get properties from and set properties in one another. The out-of-band messages are used symmetrically:
#$#client-get { property="property name" } | { properties="list of property names"} #$#client-set propertyName = propertyValue #$#client-value propertyName=propertyValue [possibly other name/value pairs]
Examples:
#$#client-get property=client.username #$#client-get properties="client.username client.password audio.version" #$#client-set client.username=Foo #$#client-value client.username=Foo #$#client-value audio.version=1.0 client.username=Foo
The #$#client-get message is sent to ask for the value of one or more properties. These values may be returned in the future through the #$#client-value message. Similarly, values may be set using #$#client-set. In our experience, it is useful to know when a value has been changed, so in such cases a #$#client-value message will be sent in response to #$#client-set.