Write a MCP server to generate template code based on a finite state machine (FSM) in which an LLM writes code for?

MCP is model context protocol. This is a relatively new method that lets our large language model hook into something in a more controlled manner via an API we’d implement that follows MCP.

A finite state machine is useful for clearly defining state and transitions between states via events. The implementation is really simple in any language. You define your transitions which are of type ordered dict<state, dict<event, state>> where state and event are str or enum. Then you iterate updating the current state given a new event until you are in a terminal state.

Example of decoding a packet: Imgur: The magic of the Internet

You can extend the FSM to associate 1 func with a transition to a state. Then we can use multi-methods in our language for implementation of behavior for the transition.

def transition_data_reading(data_event: str, reading_state: str) -> str:
    pass # LLM may read, write, update this body

# ...

Then you’d enable the MCP to permit it to read, write, update logic in each of these transition functions.

This allows us to put constraints on the code the LLM can change while guaranteeing that we are at least sticking to code that follows the FSM.

Is this useful to you in a domain you work in?

Absolutely. Seems you have defined a useful core function for building Finite State Automata. Needs more prototyping since passing and returning strings is way too little context. Eventually the strings will need more structure to handle intermediate, error recovery semantics, and long running session states.

If you want to see a live example of someone struggling with MCP type issues then check out Dave Plumber on Youtube and his Tempest AI solver. Hope he gets it to work.

One annoying thing is the node.js package for MCP has made breaking changes since I last touched this (~1 month ago) and it doesn’t seem to have local LLM support as a 1st class citizen. So I’m running with 0.5 instead of the latest 1.3 as it’s a more clear path on the dev and debug work.

I’ve mostly prototyped this as 1 layer of templating then some regex str replacing as a 2nd layer. The real challenge is to see how well the LLM can understand and implement each associated function in its capacity.

The context is tricky. I’m planning on letting Context be a module level variable then have pubsub events emitted for relevant/all properties of it for safer read-only on change or now. At some point, I will want outside to mutate the instance of Context. Maybe I could let outside queue up an event like evt:reset which gets applied as a transition to whatever state the FSM is currently in.

Some snippets

user defined FSM

{
  "init_state": "state:no_light",
  "transitions": {
    "state:no_light": { "evt:next": "state:red_light" },
    "state:red_light": { "evt:next": "state:green_light" },
    "state:green_light": { "evt:next":  "state:yellow_light" },
    "state:yellow_light": { "evt:next": "state:red_light" },

    "state:unknown_state": { "evt:redirect":  "state:no_light" }
  }
}

(separate example than above) generated from templating then injected using inference from LLM

class Context:
   balance: int = 0
   money_read: int = 0
   item_cost: int = 1

   @property
   def user_input(self) -> Generator[Event, Any, None]:
       input('press enter to randomly choose money_insert or item_select event')
       yield random.choice([Event.MONEY_INSERT, Event.ITEM_SELECT])

_context = Context()

def handle_idle(state: State.IDLE) -> State:
	return _context.user_input()

@guarantee_state
@log_decorator
def enter_state(state: State) -> State:
	handler = state_handlers.get(state, handle_unknown_state)
	return handler(state)