You can change
appV fn x = atomically $ readTVar x >>= writeTVar x . fn
to appV fn x = atomically $ modifyTVar x fn
The function getValue need not be in IO monad. You can simplify it using fromMaybe in Data.Maybe.Sometimes you check "head l" and "tail l"; it's more idiomatic to use pattern matching. For example, you can change:
setCommand handle cmd db = do
appV (conv k v) db
hPutStrLn handle $ "OK"
where k = (head cmd)
v = (unwords (tail cmd))
....
case (head cmd) of
("get") -> getCommand handle (unwords (tail cmd)) db
("set") -> setCommand handle (tail cmd) db
to something like this: setCommand handle key val db = do
appV (conv key val) db
hPutStrLn handle "OK"
...
case cmd of
"get":key -> getCommand handle (unwords key) db
"set":key:val -> setCommand handle key (unwords val) db
Also consider using hlint, it often gives good advice.