Spaces:
Sleeping
Sleeping
allow user input to decide API key in-session
Browse files
app.py
CHANGED
@@ -1,83 +1,109 @@
|
|
1 |
"""
|
2 |
-
Gradio
|
3 |
-
|
|
|
|
|
4 |
"""
|
5 |
-
import
|
|
|
6 |
|
|
|
|
|
|
|
|
|
7 |
|
8 |
-
|
9 |
-
#
|
10 |
-
#
|
11 |
-
|
|
|
12 |
"""
|
13 |
-
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
"""
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
except requests.RequestException:
|
22 |
-
pass
|
23 |
-
# last-ditch: internal docker address (may be 10.x/172.x)
|
24 |
-
return socket.gethostbyname(socket.gethostname())
|
25 |
|
26 |
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
28 |
|
|
|
29 |
|
30 |
-
|
31 |
-
|
32 |
-
#
|
33 |
-
def recent_battles(api_key: str, player_tag: str) -> dict:
|
34 |
-
api_key = api_key.strip()
|
35 |
-
tag = player_tag.strip().upper().lstrip("#")
|
36 |
|
37 |
-
|
38 |
-
|
|
|
|
|
|
|
|
|
|
|
39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
try:
|
41 |
-
client
|
42 |
-
logs
|
43 |
except brawlstats.Forbidden:
|
44 |
return {"error": "Invalid API key"}
|
45 |
except brawlstats.NotFound:
|
46 |
return {"error": f"Player #{tag} not found"}
|
47 |
except brawlstats.RequestError as e:
|
48 |
-
return {"error": f"
|
49 |
|
50 |
return {
|
51 |
-
"
|
52 |
-
"
|
53 |
-
"
|
54 |
-
"battles": logs.raw_data
|
55 |
}
|
56 |
|
57 |
|
58 |
-
#
|
59 |
-
#
|
60 |
-
#
|
61 |
-
with gr.Blocks(title="Brawl Stars
|
62 |
-
gr.
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
api_box = gr.Textbox(
|
68 |
-
label="API Key",
|
69 |
-
type="password",
|
70 |
-
placeholder="Paste your API tokenβ¦"
|
71 |
-
)
|
72 |
-
tag_box = gr.Textbox(
|
73 |
-
label="Player Tag",
|
74 |
-
placeholder="#V2LQY9UY"
|
75 |
-
)
|
76 |
-
out_json = gr.JSON(label="Battle Log")
|
77 |
-
|
78 |
-
api_box.change(lambda *_: None, inputs=None, outputs=out_json)
|
79 |
-
tag_box.submit(recent_battles, [api_box, tag_box], out_json)
|
80 |
|
81 |
-
|
82 |
-
|
|
|
|
|
|
|
83 |
|
|
|
|
|
|
|
|
|
|
|
|
1 |
"""
|
2 |
+
MCP-ready Gradio app
|
3 |
+
β’ Tool 1: save_brawlstars_key(api_key) β str
|
4 |
+
β’ Tool 2: get_recent_battles(player_tag) β dict
|
5 |
+
UI: two tabs so humans can still use the app.
|
6 |
"""
|
7 |
+
import gradio as gr
|
8 |
+
import brawlstats # pip install brawlstats
|
9 |
|
10 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββ
|
11 |
+
# GLOBAL STATE (shared by all tool invocations)
|
12 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββ
|
13 |
+
BS_API_KEY = None # will hold the token after Tool-1 runs
|
14 |
|
15 |
+
|
16 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββ
|
17 |
+
# TOOL 1 β save the key (no echo)
|
18 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββ
|
19 |
+
def save_brawlstars_key(api_key: str) -> str:
|
20 |
"""
|
21 |
+
Store a Brawl Stars API key in server memory for later use.
|
22 |
+
|
23 |
+
This should be the **first** tool called in a session. Once the key
|
24 |
+
is saved, subsequent calls to `get_recent_battles` can access the API
|
25 |
+
without requiring the LLM (or user) to resend the long token.
|
26 |
+
|
27 |
+
Args:
|
28 |
+
api_key (str): Your personal token generated at
|
29 |
+
https://developer.brawlstars.com. Do **not** share this
|
30 |
+
publiclyβsend it only once via this tool.
|
31 |
+
|
32 |
+
Returns:
|
33 |
+
str: Status message β
|
34 |
+
β’ "β
API key saved in server memory" on success
|
35 |
+
β’ "β No key provided" if the argument was empty
|
36 |
"""
|
37 |
+
global BS_API_KEY
|
38 |
+
BS_API_KEY = api_key.strip()
|
39 |
+
if BS_API_KEY:
|
40 |
+
return "β
API key saved in server memory"
|
41 |
+
return "β No key provided"
|
|
|
|
|
|
|
|
|
42 |
|
43 |
|
44 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββ
|
45 |
+
# TOOL 2 β fetch recent battles using saved key
|
46 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββ
|
47 |
+
def get_recent_battles(player_tag: str) -> dict:
|
48 |
+
"""
|
49 |
+
Retrieve a player's 25 most-recent Brawl Stars battles.
|
50 |
|
51 |
+
Call `save_brawlstars_key` **once** beforehand to cache the API key.
|
52 |
|
53 |
+
Args:
|
54 |
+
player_tag (str): The playerβs in-game tag, with or without the
|
55 |
+
leading '#', e.g. "#V2LQY9UY" or "V2LQY9UY".
|
|
|
|
|
|
|
56 |
|
57 |
+
Returns:
|
58 |
+
dict: On success β
|
59 |
+
{
|
60 |
+
"player": "#TAG",
|
61 |
+
"count": <int>, # number of battles returned
|
62 |
+
"battles": <list[dict]> # raw JSON from Supercell
|
63 |
+
}
|
64 |
|
65 |
+
On failure β
|
66 |
+
{ "error": "<human-readable message>" }
|
67 |
+
"""
|
68 |
+
if not BS_API_KEY:
|
69 |
+
return {"error": "API key not set - call save_brawlstars_key first"}
|
70 |
+
|
71 |
+
tag = player_tag.strip().upper().lstrip("#")
|
72 |
try:
|
73 |
+
client = brawlstats.Client(BS_API_KEY)
|
74 |
+
logs = client.get_battle_logs(tag) # BattleLog object
|
75 |
except brawlstats.Forbidden:
|
76 |
return {"error": "Invalid API key"}
|
77 |
except brawlstats.NotFound:
|
78 |
return {"error": f"Player #{tag} not found"}
|
79 |
except brawlstats.RequestError as e:
|
80 |
+
return {"error": f"API request failed: {e}"}
|
81 |
|
82 |
return {
|
83 |
+
"player": f"#{tag}",
|
84 |
+
"count": len(logs),
|
85 |
+
"battles": logs.raw_data # serialisable list
|
|
|
86 |
}
|
87 |
|
88 |
|
89 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββ
|
90 |
+
# HUMAN-FRIENDLY UI (optional)
|
91 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββ
|
92 |
+
with gr.Blocks(title="Brawl Stars MCP Tools") as demo:
|
93 |
+
with gr.Tab("π Save API Key"):
|
94 |
+
api_box = gr.Textbox(type="password", label="API Key")
|
95 |
+
save_btn = gr.Button("Save")
|
96 |
+
save_out = gr.Textbox(label="Status", interactive=False)
|
97 |
+
save_btn.click(save_brawlstars_key, inputs=api_box, outputs=save_out)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
|
99 |
+
with gr.Tab("π Get Recent Battles"):
|
100 |
+
tag_box = gr.Textbox(label="Player Tag", placeholder="#V2LQY9UY")
|
101 |
+
fetch_btn = gr.Button("Fetch")
|
102 |
+
json_out = gr.JSON(label="Battle Log")
|
103 |
+
fetch_btn.click(get_recent_battles, inputs=tag_box, outputs=json_out)
|
104 |
|
105 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββ
|
106 |
+
# LAUNCH AS MCP SERVER
|
107 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββ
|
108 |
+
if __name__ == "__main__":
|
109 |
+
demo.launch(mcp_server=True)
|