Skip to main content

How MCP is masked PRIMER

MCP is PRIMER in disguise: the same loop (reason → invoke → monitor → explain or recover), but standardized as a protocol. Your app’s “functions” become MCP tools (and read-only data becomes MCP resources); the MCP client runs the loop and talks to MCP servers instead of your in-process code. This page shows the PRIMER prompt, maps each part to MCP, and sketches how the implementation translates.

The PRIMER prompt and its MCP counterparts

Below is the prompt we gave the model. Each part is labeled with the MCP concept it corresponds to.

Prompt partMCP counterpart
Steps (prompt_input, reason_plan, invoke_action, monitor_result, explain_output, recover_error)Client loop: user message → model reasons → client invokes tool → gets tool result → model explains or client handles error.
invoke_action (function name + input)Client calls a tool on an MCP server with arguments.
monitor_result (function output)Tool result returned by the server to the client.
Functions (getTrainStatus, getPNRStatus)MCP tools — server advertises name, description, parameters; client invokes them.
getTrainNumberMapping() (lookup)MCP resource — read-only data the client fetches by URI, not an action.

Diagram: PRIMER prompt → MCP

flowchart LR
subgraph PRIMER[" PRIMER (your app) "]
direction TB
P(prompt_input)
R1(reason_plan)
I(invoke_action)
M(monitor_result)
E(explain_output)
R2(recover_error)
end

subgraph MCP[" MCP "]
direction TB
U(User message)
Client(MCP Client)
Tool(MCP Tool)
Result(Tool result)
Server(MCP Server)
end

P --> U
R1 --> Client
I --> Tool
M --> Result
E --> Client
R2 --> Client
U --> Client
Client --> Tool
Tool --> Server
Server --> Result
Result --> Client

style PRIMER fill:#ede9fe,stroke:#5b21b6,stroke-width:2px
style MCP fill:#ecfdf5,stroke:#0f766e,stroke-width:2px
classDef primerNode fill:#ddd6fe,stroke:#5b21b6,stroke-width:2px
classDef mcpNode fill:#a7f3d0,stroke:#0f766e,stroke-width:2px
classDef clientNode fill:#5eead4,stroke:#0d9488,stroke-width:2px
class P,R1,I,M,E,R2 primerNode
class U,Tool,Result,Server mcpNode
class Client clientNode

In words:
prompt_input → user message to the client. reason_plan → client sends conversation to model. invoke_action → client calls a tool on a server. monitor_resulttool result from server back to client. explain_output / recover_error → client shows answer or handles error.

Prompt with part-by-part mapping

Follow the PRIMER steps to handle user requests using the available functions.

MCP: Client enforces this loop

Steps:
- prompt_input: Wait for user input. → MCP: User message (client receives it)
- reason_plan: Decide which function to call. → MCP: Model reasoning (client sends to model)
- invoke_action: Call the chosen function... → MCP: Client invokes a TOOL on a server
- monitor_result: Capture and observe output. → MCP: Client receives TOOL RESULT from server
- explain_output: Format and return answer. → MCP: Model output (client shows to user)
- recover_error: Handle any issues. → MCP: Client handles errors

Functions:
1. getTrainStatus(train_no: str) → MCP: TOOL (action with parameters)
2. getPNRStatus(pnr: str) → MCP: TOOL (action with parameters)
3. getTrainNumberMapping() → MCP: RESOURCE (read/lookup by URI, not a tool)

Implementation sketch

How we implemented the loop in our app (and how MCP does the same over the wire):

  • Tools: A dictionary mapped tool names to implementations: getTrainStatus, getPNRStatus. The train-number mapping was implemented as a callable in that app, but conceptually it’s a resource (read/lookup data), not an action—in MCP you’d expose it as a resource the client reads rather than a tool it invokes.
  • Messages: Conversation starts with the system prompt; each user turn is sent as a prompt_input JSON message.
  • Loop: We call the model, parse the response as JSON, then:
    • explain_output → print the response and break (done).
    • invoke_action → look up the function in the tools dict, call it with step["input"], then append either a monitor_result (success) or recover_error (exception) message and continue the loop.
  • Errors: Invalid JSON or other parsing errors are caught and the loop breaks with a clear message.

So the “invoke” and “monitor” steps were implemented in our app: we ran the chosen function and fed the result (or error) back into the conversation as the next message. MCP does the same for tools; for things like the train-name→number mapping, MCP uses resources (read-only data the client fetches by URI) instead of a separate “tool.”

Full implementation: app.py.


Previous: What is PRIMER. For MCP concepts: MCP Core Concept.