To do this effectively without 'generating all possible options,' you're going to want to use a layering approach... at least in theory, even if in practice you eventually collapse the layered textures to a single sprite for draw-call reduction, which I think you should and will detail later.
The layering approach involves having a sprite set for all your characters without weapons or gear, and individual sprite sets for each weapon or gear aligned in such a way that they can be rendered overtop the base character sprites to form the final, composited character.
For simple sprites with few viewing angles (like the battle sprites of traditional Final Fantasy games), this is relatively easy. For more complex sprites (such as the characters of Diablo II, which had multiple facings and an isometric perspective), the weapon and armor sprites will be more difficult to construct because you must leave "gaps" in the sprite when it would appear behind the character. A simple way to alleviate that issue is to allow the equipment sprites to be further subdivided into a "foreground" and "background" layer. Your process for rendering a sprite is then logically:
- Render the gear background layers (usually only weapons).
- Render the base character sprite.
- Render the gear foreground layers (usually mainly armor).
This method is not fool-proof (especially as you increase the number of independently-interchangeable pieces of gear that can be equipped and also impact a rendered character), but it will work for a large majority of the equipment once your artists get the hang of the authoring technique.
A sprite-editing tool with toggle-able layers will be critical because it's hard to animate a sword or staff being swung through empty air without a reference layer of the character swinging it. If your sprites are actually created in a 3D tool, it can be much easier to get this pipeline set up.
You could use this technique in it's vanilla form as described above. However, you should be aware that it means increasing the number of draw calls per on-screen character significantly, which can be a serious problem in an MMO where once must at least account for the possibility that many characters will be on-screen at a time.
Baking the combined layer render into a single sprite sheet is a useful tool to combat this problem. You should have some code that will perform the above rendering steps to a texture which you can save to disk or cache in-memory. That way you can pay the increased render cost once, any time a player switches their gear, but after that initial render you switch to using the baked composite texture sheet for all subsequent frames.
For maximum runtime efficiency, make sure all the sprite sheets for character and gear align exactly so you can just render the entire sheet and not individual frames. You can use various forms of off-line bin-packing to maximize the layout of sprites on the sheet, as well as texture compression, to save off-line space.
It's also possible to leverage this technique during your game's content build: characters with fixed gear, such as NPCs, can be pre-baked to save some run-time (particularly for characters which might appear frequently).
You will also want to make sure your game as a way to report to any player the base character type and equipped gear of other players nearby, so that you can pre-emptively do the compositing on a client's machine for when those nearby players eventually come into view of the local player.