File size: 6,630 Bytes
b9f174a
1fe9b6d
b9f174a
 
 
 
 
1fe9b6d
b9f174a
 
 
 
1fe9b6d
b9f174a
 
1fe9b6d
b9f174a
 
1fe9b6d
b9f174a
 
 
1fe9b6d
b9f174a
 
 
 
 
 
1fe9b6d
b9f174a
 
 
 
1fe9b6d
b9f174a
1fe9b6d
b9f174a
 
1fe9b6d
b9f174a
1fe9b6d
b9f174a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1fe9b6d
 
b9f174a
1fe9b6d
b9f174a
 
 
 
 
 
 
 
 
1fe9b6d
b9f174a
 
 
 
1fe9b6d
 
b9f174a
 
 
 
 
 
 
 
 
 
1fe9b6d
 
b9f174a
 
 
 
 
1fe9b6d
b9f174a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1fe9b6d
 
b9f174a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1fe9b6d
 
b9f174a
 
 
 
 
 
 
1fe9b6d
b9f174a
 
 
 
 
 
 
1fe9b6d
 
 
b9f174a
1fe9b6d
b9f174a
 
 
 
 
 
1fe9b6d
 
 
 
 
b9f174a
 
 
1fe9b6d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
"""app.py — Gradio MCP: Omikuji (Fortune) App

This Gradio application exposes several "omikuji" (fortune-telling) tools as both a
visual web UI (tabbed) and Model Context Protocol (MCP) SSE endpoints. Each tool is
implemented as a standalone Python function with a rich docstring so that Gradio
can automatically generate a JSON schema for MCP clients (Claude Desktop, Cline,
etc.).

Patterns implemented
--------------------
1. draw_omikuji_basic
   └─ Returns a single traditional Japanese fortune such as "大吉", "凶" …

2. draw_omikuji_lucky_item
   └─ Adds a random lucky item (財布, 植物, …) to the basic fortune.

3. draw_omikuji_lucky_color_number
   └─ Adds a lucky colour (赤, 青 …) *and* lucky number (1-99).

4. draw_omikuji_full
   └─ Gives a comprehensive result (overall/love/money/health fortunes, lucky
      colour, item and number).  Optional `name` input personalises the message.

Running
-------
```
uv venv            # optional: create virtual-env with uv (or use pip)
source .venv/bin/activate
uv pip install -r requirements.txt

python app.py      # UI http://127.0.0.1:7860/
                  # SSE http://127.0.0.1:7860/gradio_api/mcp/sse
```
"""

from __future__ import annotations

import random
from typing import Dict

import gradio as gr

# ---------------------------------------------------------------------------
# 🎴 Omikuji base data
# ---------------------------------------------------------------------------

FORTUNES = [
    "大吉",  # Excellent luck
    "中吉",  # Good luck
    "小吉",  # Little luck
    "吉",    # Luck
    "末吉",  # Future luck
    "凶",    # Bad luck
    "大凶",  # Terrible luck
]

LUCKY_ITEMS = [
    "財布",
    "スマホ",
    "手帳",
    "時計",
    "本",
    "鉛筆",
    "植物",
    "マグカップ",
    "イヤホン",
    "傘",
]

LUCKY_COLORS = [
    "赤",
    "青",
    "緑",
    "黄色",
    "紫",
    "オレンジ",
    "ピンク",
    "白",
    "黒",
    "金",
]


# ---------------------------------------------------------------------------
# 🔮 Omikuji functions (MCP tools)
# ---------------------------------------------------------------------------


def draw_omikuji_basic() -> str:
    """Draw a single omikuji fortune.

    Returns
    -------
    str
        A traditional Japanese fortune string such as "大吉" (excellent luck)
        or "凶" (bad luck).
    """

    return random.choice(FORTUNES)


def draw_omikuji_lucky_item() -> str:
    """Draw an omikuji fortune **with a lucky item**.

    Returns
    -------
    str
        A sentence combining the fortune and a randomly chosen lucky item.
        Example: "中吉 — 今日のラッキーアイテムは『時計』!".
    """

    fortune = random.choice(FORTUNES)
    item = random.choice(LUCKY_ITEMS)
    return f"{fortune} — 今日のラッキーアイテムは『{item}』!"


def draw_omikuji_lucky_color_number() -> str:
    """Draw an omikuji fortune **with lucky colour & number**.

    Returns
    -------
    str
        A formatted string that includes:
        - Fortune (大吉, …)
        - Lucky colour (赤, 青, …)
        - Lucky number (1–99)
    """

    fortune = random.choice(FORTUNES)
    color = random.choice(LUCKY_COLORS)
    number = random.randint(1, 99)
    return f"{fortune} — ラッキーカラー: {color} / ラッキーナンバー: {number}"


def draw_omikuji_full(name: str | None = None) -> Dict[str, str]:
    """Comprehensive omikuji result.

    Parameters
    ----------
    name : str | None, optional
        User's name.  When provided the message becomes personalised.

    Returns
    -------
    dict of str
        Keys: "overall", "love", "money", "health", "lucky_color",
        "lucky_item", "lucky_number".

    Notes
    -----
    This function returns a dict so that MCP clients receive structured JSON.
    In the Gradio UI the dictionary is rendered nicely as a JSON viewer.
    """

    overall = random.choice(FORTUNES)

    # Sub-category fortunes use a biased list (no 大凶) to avoid discouragement
    sub_fortunes = FORTUNES[:-1]  # remove 大凶
    love = random.choice(sub_fortunes)
    money = random.choice(sub_fortunes)
    health = random.choice(sub_fortunes)

    return {
        "name": name or "あなた",
        "overall": overall,
        "love": love,
        "money": money,
        "health": health,
        "lucky_color": random.choice(LUCKY_COLORS),
        "lucky_item": random.choice(LUCKY_ITEMS),
        "lucky_number": str(random.randint(1, 99)),
    }


# ---------------------------------------------------------------------------
# 🖼️  Gradio UI — TabbedInterface
# ---------------------------------------------------------------------------

# Each tool is mapped to a separate Interface for clarity.  The outputs are
# chosen so that both web UI and MCP behave nicely.

iface_basic = gr.Interface(
    fn=draw_omikuji_basic,
    inputs=[],  # no user input
    outputs=gr.Textbox(label="結果"),
    api_name="draw_omikuji_basic",
    description="最もシンプルな伝統おみくじです。クリックするだけで運勢が表示されます。",
)

iface_item = gr.Interface(
    fn=draw_omikuji_lucky_item,
    inputs=[],
    outputs=gr.Textbox(label="結果"),
    api_name="draw_omikuji_lucky_item",
    description="運勢に加えてラッキーアイテムを教えてくれるおみくじです。",
)


iface_color_number = gr.Interface(
    fn=draw_omikuji_lucky_color_number,
    inputs=[],
    outputs=gr.Textbox(label="結果"),
    api_name="draw_omikuji_lucky_color_number",
    description="運勢+ラッキーカラー&ナンバーのおみくじです。",
)


iface_full = gr.Interface(
    fn=draw_omikuji_full,
    inputs=[gr.Textbox(label="名前(任意)", placeholder="入力しなくてもOK")],
    outputs=gr.JSON(label="総合結果"),
    api_name="draw_omikuji_full",
    description="総合運・恋愛運・金運・健康運とラッキー情報を一度に表示します。",
)


# Combine into tabs
demo = gr.TabbedInterface(
    interface_list=[iface_basic, iface_item, iface_color_number, iface_full],
    tab_names=[
        "シンプルおみくじ",
        "アイテム付きおみくじ",
        "カラー&ナンバーおみくじ",
        "総合おみくじ",
    ],
)


if __name__ == "__main__":
    # `mcp_server=True` exposes the functions to SSE clients at
    # /gradio_api/mcp/sse.  Passing `share=True` is optional here but useful for
    # quickly sharing the demo.
    demo.launch(mcp_server=True)