ryomo commited on
Commit
bd56ca5
Β·
1 Parent(s): ea5a5fd

refactor: separate MCP tools to mcp_tools.py

Browse files
Files changed (2) hide show
  1. app.py +5 -135
  2. src/unpredictable_lord/mcp_tools.py +147 -0
app.py CHANGED
@@ -5,19 +5,16 @@ import sys
5
  sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
6
 
7
  import logging
8
- import uuid
9
 
10
  import gradio as gr
11
  import spaces
12
 
13
  from unpredictable_lord.chat import chat_with_llm_stream
14
- from unpredictable_lord.game_state import (
15
- ADVICE_DESCRIPTIONS,
16
- PERSONALITY_DESCRIPTIONS,
17
- create_session,
18
- execute_turn_logic,
19
- get_available_advice,
20
- get_session,
21
  )
22
 
23
  logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
@@ -26,133 +23,6 @@ logger = logging.getLogger(__name__)
26
  logger.info(f"ZeroGPU: {spaces.config.Config.zero_gpu}")
27
 
28
 
29
- def init_game(personality: str = "cautious") -> dict:
30
- """
31
- Initialize a new game session and return the session information.
32
-
33
- This MCP tool creates a new game session with the specified lord personality.
34
- Use the returned session_id for all subsequent game operations.
35
-
36
- Args:
37
- personality: The lord's personality type.
38
- Options: "cautious" (risk-averse), "idealist" (emotional idealist),
39
- "populist" (popularity-focused). Defaults to "cautious".
40
-
41
- Returns:
42
- dict: Session information including session_id and initial game state.
43
- """
44
- session_id = str(uuid.uuid4())
45
-
46
- # Validate personality
47
- if personality not in PERSONALITY_DESCRIPTIONS:
48
- personality = "cautious"
49
-
50
- # Create game session using game_state module
51
- state = create_session(session_id, personality)
52
-
53
- logger.info(
54
- f"New game session created: {session_id} with personality: {personality}"
55
- )
56
-
57
- return {
58
- "session_id": session_id,
59
- "message": f"New game started! You are now the advisor to a {personality} lord.",
60
- "personality_description": PERSONALITY_DESCRIPTIONS[personality],
61
- "available_advice": ADVICE_DESCRIPTIONS,
62
- "game_state": state.to_dict(),
63
- }
64
-
65
-
66
- def get_game_state(session_id: str) -> dict:
67
- """
68
- Get the current game state for a session.
69
-
70
- This MCP tool retrieves the current state of a game session,
71
- including all parameters, turn number, and game status.
72
-
73
- Args:
74
- session_id: The session ID returned from init_game.
75
-
76
- Returns:
77
- dict: Current game state or error message if session not found.
78
- """
79
- state = get_session(session_id)
80
-
81
- if state is None:
82
- return {
83
- "error": "Session not found",
84
- "message": f"No game session found with ID: {session_id}. Please call init_game first.",
85
- }
86
-
87
- return {
88
- "session_id": session_id,
89
- "game_state": state.to_dict(),
90
- "status_summary": state.get_status_summary(),
91
- }
92
-
93
-
94
- def list_available_advice() -> dict:
95
- """
96
- Get all available advice options that can be given to the lord.
97
-
98
- This MCP tool returns a list of all possible advice types that can be
99
- used with execute_turn(). The Lord AI should interpret the user's
100
- free-form suggestion and map it to one of these options.
101
-
102
- Returns:
103
- dict: Dictionary containing all available advice options with
104
- their names, descriptions, and expected effects.
105
- """
106
- return {
107
- "advice_options": get_available_advice(),
108
- "usage": "Interpret the user's advice and select the most appropriate option from the list above.",
109
- }
110
-
111
-
112
- def execute_turn(session_id: str, advice: str) -> dict:
113
- """
114
- Execute a turn with the given advice.
115
-
116
- This MCP tool is called by the Lord AI after interpreting the user's suggestion.
117
- The lord will decide whether to follow the advice based on personality and trust.
118
-
119
- Args:
120
- session_id: The session ID returned from init_game.
121
- advice: The advice type to execute. Must be one of:
122
- - "increase_tax": Raise taxes (Treasury ↑, Satisfaction ↓)
123
- - "decrease_tax": Lower taxes (Treasury ↓, Satisfaction ↑)
124
- - "expand_territory": Military expansion (Territory ↑, Treasury ↓, risky)
125
- - "improve_diplomacy": Diplomatic efforts (Royal Trust ↑, Treasury ↓)
126
- - "public_festival": Hold festival (Satisfaction ↑, Treasury ↓)
127
- - "build_infrastructure": Build infrastructure (Population ↑, Treasury ↓)
128
- - "do_nothing": Maintain current state
129
-
130
- Returns:
131
- dict: Result containing:
132
- - adopted: Whether the lord followed the advice
133
- - action_taken: The actual action the lord took
134
- - parameter_changes: Changes to game parameters
135
- - game_over: Whether the game has ended
136
- - new_state: Updated game state
137
- """
138
- state = get_session(session_id)
139
-
140
- if state is None:
141
- return {
142
- "error": "Session not found",
143
- "message": f"No game session found with ID: {session_id}. Please call init_game first.",
144
- }
145
-
146
- result = execute_turn_logic(state, advice)
147
-
148
- logger.info(
149
- f"Turn executed for session {session_id}: advice={advice}, "
150
- f"adopted={result.get('adopted')}, action={result.get('action_taken')}"
151
- )
152
-
153
- return result
154
-
155
-
156
  # Gradio UI
157
  with gr.Blocks(title="Unpredictable Lord") as demo:
158
  gr.Markdown("# Unpredictable Lord\nLord Advisor AI Simulation")
 
5
  sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
6
 
7
  import logging
 
8
 
9
  import gradio as gr
10
  import spaces
11
 
12
  from unpredictable_lord.chat import chat_with_llm_stream
13
+ from unpredictable_lord.mcp_tools import (
14
+ execute_turn,
15
+ get_game_state,
16
+ init_game,
17
+ list_available_advice,
 
 
18
  )
19
 
20
  logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
 
23
  logger.info(f"ZeroGPU: {spaces.config.Config.zero_gpu}")
24
 
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  # Gradio UI
27
  with gr.Blocks(title="Unpredictable Lord") as demo:
28
  gr.Markdown("# Unpredictable Lord\nLord Advisor AI Simulation")
src/unpredictable_lord/mcp_tools.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ MCP tools for Unpredictable Lord game.
3
+
4
+ This module contains all MCP tool functions that are exposed
5
+ to external LLM clients via the Gradio MCP server.
6
+ """
7
+
8
+ import logging
9
+ import uuid
10
+
11
+ from unpredictable_lord.game_state import (
12
+ ADVICE_DESCRIPTIONS,
13
+ PERSONALITY_DESCRIPTIONS,
14
+ create_session,
15
+ execute_turn_logic,
16
+ get_available_advice,
17
+ get_session,
18
+ )
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ def init_game(personality: str = "cautious") -> dict:
24
+ """
25
+ Initialize a new game session and return the session information.
26
+
27
+ This MCP tool creates a new game session with the specified lord personality.
28
+ Use the returned session_id for all subsequent game operations.
29
+
30
+ Args:
31
+ personality: The lord's personality type.
32
+ Options: "cautious" (risk-averse), "idealist" (emotional idealist),
33
+ "populist" (popularity-focused). Defaults to "cautious".
34
+
35
+ Returns:
36
+ dict: Session information including session_id and initial game state.
37
+ """
38
+ session_id = str(uuid.uuid4())
39
+
40
+ # Validate personality
41
+ if personality not in PERSONALITY_DESCRIPTIONS:
42
+ personality = "cautious"
43
+
44
+ # Create game session using game_state module
45
+ state = create_session(session_id, personality)
46
+
47
+ logger.info(
48
+ f"New game session created: {session_id} with personality: {personality}"
49
+ )
50
+
51
+ return {
52
+ "session_id": session_id,
53
+ "message": f"New game started! You are now the advisor to a {personality} lord.",
54
+ "personality_description": PERSONALITY_DESCRIPTIONS[personality],
55
+ "available_advice": ADVICE_DESCRIPTIONS,
56
+ "game_state": state.to_dict(),
57
+ }
58
+
59
+
60
+ def get_game_state(session_id: str) -> dict:
61
+ """
62
+ Get the current game state for a session.
63
+
64
+ This MCP tool retrieves the current state of a game session,
65
+ including all parameters, turn number, and game status.
66
+
67
+ Args:
68
+ session_id: The session ID returned from init_game.
69
+
70
+ Returns:
71
+ dict: Current game state or error message if session not found.
72
+ """
73
+ state = get_session(session_id)
74
+
75
+ if state is None:
76
+ return {
77
+ "error": "Session not found",
78
+ "message": f"No game session found with ID: {session_id}. Please call init_game first.",
79
+ }
80
+
81
+ return {
82
+ "session_id": session_id,
83
+ "game_state": state.to_dict(),
84
+ "status_summary": state.get_status_summary(),
85
+ }
86
+
87
+
88
+ def list_available_advice() -> dict:
89
+ """
90
+ Get all available advice options that can be given to the lord.
91
+
92
+ This MCP tool returns a list of all possible advice types that can be
93
+ used with execute_turn(). The Lord AI should interpret the user's
94
+ free-form suggestion and map it to one of these options.
95
+
96
+ Returns:
97
+ dict: Dictionary containing all available advice options with
98
+ their names, descriptions, and expected effects.
99
+ """
100
+ return {
101
+ "advice_options": get_available_advice(),
102
+ "usage": "Interpret the user's advice and select the most appropriate option from the list above.",
103
+ }
104
+
105
+
106
+ def execute_turn(session_id: str, advice: str) -> dict:
107
+ """
108
+ Execute a turn with the given advice.
109
+
110
+ This MCP tool is called by the Lord AI after interpreting the user's suggestion.
111
+ The lord will decide whether to follow the advice based on personality and trust.
112
+
113
+ Args:
114
+ session_id: The session ID returned from init_game.
115
+ advice: The advice type to execute. Must be one of:
116
+ - "increase_tax": Raise taxes (Treasury ↑, Satisfaction ↓)
117
+ - "decrease_tax": Lower taxes (Treasury ↓, Satisfaction ↑)
118
+ - "expand_territory": Military expansion (Territory ↑, Treasury ↓, risky)
119
+ - "improve_diplomacy": Diplomatic efforts (Royal Trust ↑, Treasury ↓)
120
+ - "public_festival": Hold festival (Satisfaction ↑, Treasury ↓)
121
+ - "build_infrastructure": Build infrastructure (Population ↑, Treasury ↓)
122
+ - "do_nothing": Maintain current state
123
+
124
+ Returns:
125
+ dict: Result containing:
126
+ - adopted: Whether the lord followed the advice
127
+ - action_taken: The actual action the lord took
128
+ - parameter_changes: Changes to game parameters
129
+ - game_over: Whether the game has ended
130
+ - new_state: Updated game state
131
+ """
132
+ state = get_session(session_id)
133
+
134
+ if state is None:
135
+ return {
136
+ "error": "Session not found",
137
+ "message": f"No game session found with ID: {session_id}. Please call init_game first.",
138
+ }
139
+
140
+ result = execute_turn_logic(state, advice)
141
+
142
+ logger.info(
143
+ f"Turn executed for session {session_id}: advice={advice}, "
144
+ f"adopted={result.get('adopted')}, action={result.get('action_taken')}"
145
+ )
146
+
147
+ return result