soko-mcp 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
soko_mcp-0.1.0/LICENSE ADDED
@@ -0,0 +1,3 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gabriel Mahia / AI Kung Fu LLC.
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.4
2
+ Name: soko-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server for East Africa commodity price intelligence and market signals
5
+ Author-email: Gabriel Mahia <contact@aikungfu.dev>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/gabrielmahia/soko-mcp
8
+ Project-URL: Repository, https://github.com/gabrielmahia/soko-mcp
9
+ Project-URL: Issues, https://github.com/gabrielmahia/soko-mcp/issues
10
+ Keywords: mcp,commodity,kenya,africa,markets,prices,agriculture,soko
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
16
+ Classifier: Topic :: Office/Business :: Financial
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: fastmcp>=2.0.0
21
+ Dynamic: license-file
22
+
23
+ # ๐Ÿ“Š soko-mcp โ€” Kenya Commodity Price Intelligence MCP Server
24
+
25
+ **First commodity price intelligence MCP server for East Africa.**
26
+
27
+ *Soko* = market in Swahili.
28
+
29
+ A farmer in Nakuru doesn't know that maize prices in Nairobi are 40% higher that week. Traders know. Farmers don't. This information asymmetry is one of the most costly structural disadvantages facing smallholder farmers. soko-mcp closes it.
30
+
31
+ ## The Structural Problem
32
+
33
+ In mature commodity markets, price discovery is instantaneous โ€” futures markets, satellite price tickers, and SMS alerts exist for every major exchange. A grain elevator in Iowa checks live CME prices before making any offer.
34
+
35
+ In Kenya, most smallholder farmers receive the price the trader offers, with no independent benchmark to compare against. The result: systematic underpricing at harvest, systematic overpricing at planting.
36
+
37
+ **Information asymmetry is a tax on the poor.**
38
+
39
+ ## Tools
40
+
41
+ | Tool | What it does |
42
+ |------|-------------|
43
+ | `commodity_price_query` | Current price for commodity at a specific market |
44
+ | `regional_price_comparison` | Compare prices across all major Kenya markets |
45
+ | `price_trend_analysis` | 6-month history + 3-month forecast with seasonal model |
46
+ | `sell_hold_decision` | Optimal sell/hold timing given storage costs and price trend |
47
+ | `market_overview` | Multi-commodity price snapshot for a market |
48
+
49
+ ## Quick Start
50
+
51
+ ```bash
52
+ pip install soko-mcp # coming soon to PyPI
53
+ soko-mcp # starts on stdio
54
+ ```
55
+
56
+ ## Example Queries for Claude
57
+
58
+ ```
59
+ "What is the current Nairobi maize price?"
60
+ "Should I sell my 50 bags of beans now or wait 2 months?"
61
+ "Compare potato prices across all Kenya markets"
62
+ "Give me a price trend for avocados in Nakuru for the next 3 months"
63
+ ```
64
+
65
+ ## Research Basis
66
+
67
+ - EAGC East Africa Regional Market Monitor
68
+ - World Bank "Information and Communication Technology and Agricultural Markets" (2016)
69
+ - Suri & Jack "Mobile Phones and Agricultural Performance" (2016)
70
+
71
+ โš ๏ธ DEMO data โ€” synthetic seasonal model. Verify at eagc.org, kalro.org, or local market boards.
72
+
73
+ ---
74
+ *ยฉ 2026 Gabriel Mahia / AI Kung Fu LLC ยท MIT License*
@@ -0,0 +1,52 @@
1
+ # ๐Ÿ“Š soko-mcp โ€” Kenya Commodity Price Intelligence MCP Server
2
+
3
+ **First commodity price intelligence MCP server for East Africa.**
4
+
5
+ *Soko* = market in Swahili.
6
+
7
+ A farmer in Nakuru doesn't know that maize prices in Nairobi are 40% higher that week. Traders know. Farmers don't. This information asymmetry is one of the most costly structural disadvantages facing smallholder farmers. soko-mcp closes it.
8
+
9
+ ## The Structural Problem
10
+
11
+ In mature commodity markets, price discovery is instantaneous โ€” futures markets, satellite price tickers, and SMS alerts exist for every major exchange. A grain elevator in Iowa checks live CME prices before making any offer.
12
+
13
+ In Kenya, most smallholder farmers receive the price the trader offers, with no independent benchmark to compare against. The result: systematic underpricing at harvest, systematic overpricing at planting.
14
+
15
+ **Information asymmetry is a tax on the poor.**
16
+
17
+ ## Tools
18
+
19
+ | Tool | What it does |
20
+ |------|-------------|
21
+ | `commodity_price_query` | Current price for commodity at a specific market |
22
+ | `regional_price_comparison` | Compare prices across all major Kenya markets |
23
+ | `price_trend_analysis` | 6-month history + 3-month forecast with seasonal model |
24
+ | `sell_hold_decision` | Optimal sell/hold timing given storage costs and price trend |
25
+ | `market_overview` | Multi-commodity price snapshot for a market |
26
+
27
+ ## Quick Start
28
+
29
+ ```bash
30
+ pip install soko-mcp # coming soon to PyPI
31
+ soko-mcp # starts on stdio
32
+ ```
33
+
34
+ ## Example Queries for Claude
35
+
36
+ ```
37
+ "What is the current Nairobi maize price?"
38
+ "Should I sell my 50 bags of beans now or wait 2 months?"
39
+ "Compare potato prices across all Kenya markets"
40
+ "Give me a price trend for avocados in Nakuru for the next 3 months"
41
+ ```
42
+
43
+ ## Research Basis
44
+
45
+ - EAGC East Africa Regional Market Monitor
46
+ - World Bank "Information and Communication Technology and Agricultural Markets" (2016)
47
+ - Suri & Jack "Mobile Phones and Agricultural Performance" (2016)
48
+
49
+ โš ๏ธ DEMO data โ€” synthetic seasonal model. Verify at eagc.org, kalro.org, or local market boards.
50
+
51
+ ---
52
+ *ยฉ 2026 Gabriel Mahia / AI Kung Fu LLC ยท MIT License*
@@ -0,0 +1,33 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "soko-mcp"
7
+ version = "0.1.0"
8
+ description = "MCP server for East Africa commodity price intelligence and market signals"
9
+ authors = [{name = "Gabriel Mahia", email = "contact@aikungfu.dev"}]
10
+ license = {text = "MIT"}
11
+ readme = "README.md"
12
+ requires-python = ">=3.9"
13
+ keywords = ["mcp", "commodity", "kenya", "africa", "markets", "prices", "agriculture", "soko"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
20
+ "Topic :: Office/Business :: Financial",
21
+ ]
22
+ dependencies = ["fastmcp>=2.0.0"]
23
+
24
+ [project.urls]
25
+ Homepage = "https://github.com/gabrielmahia/soko-mcp"
26
+ Repository = "https://github.com/gabrielmahia/soko-mcp"
27
+ Issues = "https://github.com/gabrielmahia/soko-mcp/issues"
28
+
29
+ [project.scripts]
30
+ soko-mcp = "soko_mcp.main:main"
31
+
32
+ [tool.setuptools.packages.find]
33
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,2 @@
1
+ """soko-mcp: Kenya Commodity Price Intelligence MCP Server"""
2
+ __version__ = "0.1.0"
@@ -0,0 +1,328 @@
1
+ #!/usr/bin/env python3
2
+ # soko-mcp โ€” Commodity Price Intelligence MCP Server
3
+ # ยฉ 2026 Gabriel Mahia / AI Kung Fu LLC โ€” MIT License
4
+ #
5
+ # The problem: Kenya smallholder farmers make sell/hold decisions with zero price data.
6
+ # A maize farmer in Nakuru doesn't know that Nairobi prices are 40% higher that week.
7
+ # The information asymmetry flows upward โ€” traders know, farmers don't.
8
+ # Western parallel: CME Group commodity futures, USDA NASS reports, Reuters Commodities
9
+ # African context: EAGC (East Africa Grain Council) collects data but it's hard to access
10
+ # Research: "Information and Communication Technology and Agricultural Markets" (World Bank, 2016)
11
+ # "Mobile Phones and Agricultural Performance" (Suri & Jack, 2016)
12
+ #
13
+ # TRUST INTEGRITY: All prices are DEMO/synthetic. Verify at EAGC, KALRO, or county markets.
14
+ # =============================================================================
15
+
16
+ from __future__ import annotations
17
+ import datetime
18
+ import random
19
+ from typing import Annotated
20
+ from fastmcp import FastMCP
21
+
22
+ mcp = FastMCP(
23
+ name="soko-mcp",
24
+ instructions="""Kenya commodity price intelligence MCP server.
25
+ Provides tools for market price queries, regional comparisons, trend analysis,
26
+ and sell/hold decision support for farmers and traders.
27
+
28
+ IMPORTANT: All prices are DEMO/synthetic for educational purposes.
29
+ Real prices: EAGC (eagc.org), KALRO (kalro.org), county market boards.
30
+ Not affiliated with any commodity exchange.
31
+ """,
32
+ )
33
+
34
+ TODAY = datetime.date.today()
35
+ MONTH = TODAY.month
36
+
37
+ # DEMO commodity price database (KES per 90kg bag unless noted)
38
+ # Seasonal patterns built in. All DEMO โ€” verify at eagc.org, kalro.org
39
+ _BASE_PRICES = {
40
+ "maize": {"nairobi": 3200, "nakuru": 2800, "eldoret": 2600, "kisumu": 3000, "mombasa": 3500, "unit": "90kg bag"},
41
+ "beans": {"nairobi": 12000, "nakuru": 10500, "eldoret": 10000, "kisumu": 11000, "mombasa": 12500, "unit": "90kg bag"},
42
+ "wheat": {"nairobi": 4500, "nakuru": 4200, "eldoret": 4000, "kisumu": 4600, "mombasa": 4800, "unit": "90kg bag"},
43
+ "potatoes": {"nairobi": 2800, "nakuru": 1800, "eldoret": 1600, "kisumu": 2500, "mombasa": 3000, "unit": "50kg bag"},
44
+ "tomatoes": {"nairobi": 4000, "nakuru": 2500, "eldoret": 2200, "kisumu": 3500, "mombasa": 4500, "unit": "crate (64kg)"},
45
+ "avocados": {"nairobi": 8000, "nakuru": 6000, "eldoret": 5500, "kisumu": 7000, "mombasa": 9000, "unit": "bag (60 pieces)"},
46
+ "tea_bulk": {"nairobi": 200, "nakuru": 180, "eldoret": 170, "kisumu": 210, "mombasa": 220, "unit": "kg"},
47
+ "coffee_parchment": {"nairobi": 120, "nakuru": 100, "eldoret": 95, "kisumu": 110, "mombasa": 125, "unit": "kg"},
48
+ "sugarcane": {"nairobi": 0, "nakuru": 4200, "eldoret": 3800, "kisumu": 4500, "mombasa": 0, "unit": "tonne"},
49
+ "onions": {"nairobi": 6000, "nakuru": 4500, "eldoret": 4000, "kisumu": 5500, "mombasa": 6500, "unit": "50kg bag"},
50
+ "kale_sukuma": {"nairobi": 30, "nakuru": 20, "eldoret": 18, "kisumu": 25, "mombasa": 35, "unit": "bunch"},
51
+ "milk": {"nairobi": 60, "nakuru": 45, "eldoret": 40, "kisumu": 50, "mombasa": 65, "unit": "litre"},
52
+ "chicken": {"nairobi": 600, "nakuru": 500, "eldoret": 450, "kisumu": 550, "mombasa": 650, "unit": "kg live"},
53
+ "eggs": {"nairobi": 15, "nakuru": 13, "eldoret": 12, "kisumu": 14, "mombasa": 16, "unit": "piece"},
54
+ }
55
+
56
+ # Seasonal multipliers (month index 1-12)
57
+ # Long rains harvest (June-July): +supply = lower prices
58
+ # Short rains harvest (Dec-Jan): +supply = lower prices
59
+ # Lean seasons (Feb-May, Aug-Nov): higher prices
60
+ _SEASONAL = {
61
+ 1: 0.90, 2: 1.10, 3: 1.15, 4: 1.10, 5: 1.05,
62
+ 6: 0.85, 7: 0.80, 8: 1.00, 9: 1.05, 10: 1.10,
63
+ 11: 1.05, 12: 0.90
64
+ }
65
+
66
+ def _price(commodity, market):
67
+ base = _BASE_PRICES.get(commodity, {}).get(market.lower())
68
+ if not base: return None
69
+ return int(base * _SEASONAL.get(MONTH, 1.0))
70
+
71
+
72
+ @mcp.tool(
73
+ description=(
74
+ "Query current commodity prices at Kenya markets. "
75
+ "Western parallel: CME Group spot price query, USDA Agricultural Marketing Service. "
76
+ "DEMO prices โ€” verify at eagc.org or county market boards."
77
+ ),
78
+ annotations={"readOnlyHint": True},
79
+ )
80
+ def commodity_price_query(
81
+ commodity: Annotated[str, "Commodity: maize, beans, wheat, potatoes, tomatoes, avocados, tea_bulk, coffee_parchment, sugarcane, onions, kale_sukuma, milk, chicken, eggs"],
82
+ market: Annotated[str, "Market: nairobi, nakuru, eldoret, kisumu, mombasa"],
83
+ ) -> dict:
84
+ price = _price(commodity.lower(), market.lower())
85
+ if price is None:
86
+ available = list(_BASE_PRICES.get(commodity.lower(), {}).keys())
87
+ return {"status": "NOT_FOUND",
88
+ "message": f"No data for {commodity} in {market}",
89
+ "markets_with_data": available}
90
+
91
+ info = _BASE_PRICES[commodity.lower()]
92
+ season = ("HARVEST (supply high, prices lower)" if MONTH in (6,7,12,1)
93
+ else "LEAN (supply lower, prices higher)" if MONTH in (2,3,4,5,8,9,10,11)
94
+ else "MID-SEASON")
95
+
96
+ return {
97
+ "status": "OK",
98
+ "commodity": commodity,
99
+ "market": market,
100
+ "price_kes": price,
101
+ "unit": info["unit"],
102
+ "date": TODAY.isoformat(),
103
+ "season": season,
104
+ "note": "DEMO โ€” Synthetic price. Verify at eagc.org, KALRO, or the local market board.",
105
+ "source": "soko-mcp DEMO dataset. Reference: EAGC market data format.",
106
+ }
107
+
108
+
109
+ @mcp.tool(
110
+ description=(
111
+ "Compare commodity prices across all Kenya markets to find the best price. "
112
+ "Western parallel: CME regional basis reports, DTN ProphetX. "
113
+ "This is the 'should I take my maize to Nairobi or sell locally?' tool. "
114
+ "DEMO prices."
115
+ ),
116
+ annotations={"readOnlyHint": True},
117
+ )
118
+ def regional_price_comparison(
119
+ commodity: Annotated[str, "Commodity to compare (maize, beans, wheat, potatoes, avocados, etc.)"],
120
+ farmer_location: Annotated[str, "Farmer's location/county (for transport cost context)"],
121
+ ) -> dict:
122
+ markets = ["nairobi", "nakuru", "eldoret", "kisumu", "mombasa"]
123
+ prices = {}
124
+ for m in markets:
125
+ p = _price(commodity.lower(), m)
126
+ if p: prices[m] = p
127
+
128
+ if not prices:
129
+ return {"status": "NOT_FOUND", "message": f"No regional data for {commodity}"}
130
+
131
+ sorted_markets = sorted(prices.items(), key=lambda x: x[1], reverse=True)
132
+ best_market, best_price = sorted_markets[0]
133
+ worst_market, worst_price = sorted_markets[-1]
134
+ spread_kes = best_price - worst_price
135
+ spread_pct = round(spread_kes / worst_price * 100, 1)
136
+
137
+ unit = _BASE_PRICES.get(commodity.lower(), {}).get("unit", "unit")
138
+
139
+ return {
140
+ "status": "OK",
141
+ "commodity": commodity,
142
+ "comparison_date": TODAY.isoformat(),
143
+ "prices_by_market": {m: {"price_kes": p, "unit": unit} for m, p in sorted_markets},
144
+ "best_market": {"market": best_market, "price_kes": best_price},
145
+ "worst_market": {"market": worst_market, "price_kes": worst_price},
146
+ "price_spread": {"kes": spread_kes, "percent": f"{spread_pct}%"},
147
+ "transport_note": (
148
+ f"Price spread of KES {spread_kes:,} across markets. "
149
+ f"Transport from {farmer_location} to {best_market}: estimate KES 2,000-8,000/tonne "
150
+ f"(actual: check county cooperatives or boda boda aggregators). "
151
+ f"Net benefit calculation required before deciding to transport."
152
+ ),
153
+ "arbitrage_opportunity": spread_pct > 15,
154
+ "note": "DEMO โ€” Synthetic prices. Verify at eagc.org before making transport decisions.",
155
+ "source": "soko-mcp DEMO dataset.",
156
+ }
157
+
158
+
159
+ @mcp.tool(
160
+ description=(
161
+ "Analyse price trends for a commodity to inform sell/hold timing. "
162
+ "Western parallel: CME seasonal charts, DTN market commentary. "
163
+ "DEMO historical simulation."
164
+ ),
165
+ annotations={"readOnlyHint": True},
166
+ )
167
+ def price_trend_analysis(
168
+ commodity: Annotated[str, "Commodity to analyse"],
169
+ market: Annotated[str, "Market: nairobi, nakuru, eldoret, kisumu, mombasa"],
170
+ months_ahead: Annotated[int, "Forecast horizon in months (1-6)"] = 3,
171
+ ) -> dict:
172
+ base = _BASE_PRICES.get(commodity.lower(), {}).get(market.lower())
173
+ if not base:
174
+ return {"status": "NOT_FOUND", "message": f"No data for {commodity} in {market}"}
175
+
176
+ # Build 6-month history and 3-month forecast using seasonal model
177
+ history = []
178
+ for i in range(6, 0, -1):
179
+ m = ((MONTH - i - 1) % 12) + 1
180
+ hist_price = int(base * _SEASONAL.get(m, 1.0))
181
+ history.append({
182
+ "month": (TODAY.replace(day=1) - datetime.timedelta(days=30*i)).strftime("%Y-%m"),
183
+ "price_kes": hist_price
184
+ })
185
+
186
+ forecast = []
187
+ for i in range(1, months_ahead + 1):
188
+ m = ((MONTH + i - 1) % 12) + 1
189
+ fcast_price = int(base * _SEASONAL.get(m, 1.0))
190
+ forecast.append({
191
+ "month": (TODAY.replace(day=1) + datetime.timedelta(days=30*i)).strftime("%Y-%m"),
192
+ "price_kes": fcast_price,
193
+ "confidence": "LOW โ€” demo seasonal model only"
194
+ })
195
+
196
+ current_price = _price(commodity.lower(), market.lower())
197
+ next_month_price = forecast[0]["price_kes"] if forecast else current_price
198
+ trend = "RISING" if next_month_price > current_price * 1.03 else \
199
+ "FALLING" if next_month_price < current_price * 0.97 else "STABLE"
200
+
201
+ unit = _BASE_PRICES.get(commodity.lower(), {}).get("unit", "unit")
202
+
203
+ return {
204
+ "status": "OK",
205
+ "commodity": commodity,
206
+ "market": market,
207
+ "current_price_kes": current_price,
208
+ "unit": unit,
209
+ "price_trend": trend,
210
+ "6_month_history": history,
211
+ f"{months_ahead}_month_forecast": forecast,
212
+ "trend_driver": {
213
+ "RISING": "Post-harvest supplies decreasing. Lean season approaching. Prices typically rise.",
214
+ "FALLING": "Harvest season approaching. Increased supply expected. Prices typically fall.",
215
+ "STABLE": "Mid-season. No major supply/demand shift expected in near term.",
216
+ }.get(trend, ""),
217
+ "warning": "DEMO โ€” Seasonal model only. Real prices affected by weather, exports, government policy, currency. Verify at eagc.org.",
218
+ "source": "soko-mcp DEMO seasonal model. Reference: EAGC East Africa Regional Market Monitor.",
219
+ }
220
+
221
+
222
+ @mcp.tool(
223
+ description=(
224
+ "Give a sell/hold recommendation for a commodity based on price trends. "
225
+ "Western parallel: CME basis trading decision tools, grain elevator advisory. "
226
+ "This is the 'what should I do with my harvest today?' AI advisor. "
227
+ "DEMO model โ€” always verify prices before acting."
228
+ ),
229
+ annotations={"readOnlyHint": True},
230
+ )
231
+ def sell_hold_decision(
232
+ commodity: Annotated[str, "Your commodity"],
233
+ market: Annotated[str, "Your nearest market"],
234
+ quantity_bags: Annotated[int, "Quantity you want to sell (bags or units)"],
235
+ storage_cost_per_month_kes: Annotated[int, "Your storage cost per month in KES (0 if storing is free)"] = 0,
236
+ months_can_store: Annotated[int, "Maximum months you can store the commodity"] = 3,
237
+ ) -> dict:
238
+ current = _price(commodity.lower(), market.lower())
239
+ if not current:
240
+ return {"status": "NOT_FOUND", "message": f"No data for {commodity} in {market}"}
241
+
242
+ unit = _BASE_PRICES.get(commodity.lower(), {}).get("unit", "unit")
243
+ total_current_value = current * quantity_bags
244
+ storage_total = storage_cost_per_month_kes * months_can_store
245
+
246
+ # Project future prices
247
+ future_prices = []
248
+ for i in range(1, months_can_store + 1):
249
+ m = ((MONTH + i - 1) % 12) + 1
250
+ base = _BASE_PRICES[commodity.lower()][market.lower()]
251
+ fp = int(base * _SEASONAL.get(m, 1.0))
252
+ net_gain = (fp - current) * quantity_bags - (storage_cost_per_month_kes * i)
253
+ future_prices.append({
254
+ "months_from_now": i,
255
+ "forecast_price_kes": fp,
256
+ "projected_value_kes": fp * quantity_bags,
257
+ "storage_cost_kes": storage_cost_per_month_kes * i,
258
+ "net_gain_vs_selling_now_kes": net_gain,
259
+ })
260
+
261
+ best_option = max(future_prices, key=lambda x: x["net_gain_vs_selling_now_kes"])
262
+ sell_now_better = best_option["net_gain_vs_selling_now_kes"] <= 0
263
+
264
+ recommendation = "SELL NOW" if sell_now_better else f"HOLD {best_option['months_from_now']} MONTHS"
265
+ rationale = (
266
+ f"Selling now yields KES {total_current_value:,}. "
267
+ + (f"Expected price rise in {best_option['months_from_now']} months would add KES {best_option['net_gain_vs_selling_now_kes']:,} after storage costs."
268
+ if not sell_now_better else
269
+ "Storage costs exceed the expected price increase. Sell now.")
270
+ )
271
+
272
+ return {
273
+ "status": "OK",
274
+ "commodity": commodity,
275
+ "market": market,
276
+ "quantity": f"{quantity_bags} {unit}s",
277
+ "current_value_kes": total_current_value,
278
+ "recommendation": recommendation,
279
+ "rationale": rationale,
280
+ "scenarios": future_prices,
281
+ "caveat": "DEMO model โ€” based on seasonal averages only. Real decisions require checking actual market prices, storage quality, buyer contracts, and weather forecasts.",
282
+ "verify_prices_at": ["eagc.org", "kalro.org", f"County Market Coordinator โ€” {market} area"],
283
+ "source": "soko-mcp DEMO seasonal model.",
284
+ }
285
+
286
+
287
+ @mcp.tool(
288
+ description=(
289
+ "Query commodity prices for multiple markets and commodities in one call. "
290
+ "Useful for traders comparing portfolios across regions. "
291
+ "DEMO data."
292
+ ),
293
+ annotations={"readOnlyHint": True},
294
+ )
295
+ def market_overview(
296
+ commodities: Annotated[str, "Comma-separated list of commodities (e.g., 'maize,beans,potatoes')"],
297
+ market: Annotated[str, "Primary market to query"] = "nairobi",
298
+ ) -> dict:
299
+ commodity_list = [c.strip().lower() for c in commodities.split(",")]
300
+ results = {}
301
+ for c in commodity_list:
302
+ p = _price(c, market.lower())
303
+ if p:
304
+ unit = _BASE_PRICES.get(c, {}).get("unit", "?")
305
+ results[c] = {"price_kes": p, "unit": unit}
306
+
307
+ season_note = {
308
+ True: "HARVEST SEASON โ€” supplies high, prices typically below annual average",
309
+ False: "LEAN SEASON โ€” supplies lower, prices typically above annual average",
310
+ }[MONTH in (6, 7, 12, 1)]
311
+
312
+ return {
313
+ "status": "OK",
314
+ "market": market,
315
+ "date": TODAY.isoformat(),
316
+ "season_context": season_note,
317
+ "prices": results,
318
+ "commodities_not_found": [c for c in commodity_list if c not in results],
319
+ "note": "DEMO โ€” Synthetic prices. Verify at eagc.org or county market boards.",
320
+ "source": "soko-mcp DEMO dataset.",
321
+ }
322
+
323
+
324
+ def main():
325
+ mcp.run()
326
+
327
+ if __name__ == "__main__":
328
+ main()
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.4
2
+ Name: soko-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server for East Africa commodity price intelligence and market signals
5
+ Author-email: Gabriel Mahia <contact@aikungfu.dev>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/gabrielmahia/soko-mcp
8
+ Project-URL: Repository, https://github.com/gabrielmahia/soko-mcp
9
+ Project-URL: Issues, https://github.com/gabrielmahia/soko-mcp/issues
10
+ Keywords: mcp,commodity,kenya,africa,markets,prices,agriculture,soko
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
16
+ Classifier: Topic :: Office/Business :: Financial
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: fastmcp>=2.0.0
21
+ Dynamic: license-file
22
+
23
+ # ๐Ÿ“Š soko-mcp โ€” Kenya Commodity Price Intelligence MCP Server
24
+
25
+ **First commodity price intelligence MCP server for East Africa.**
26
+
27
+ *Soko* = market in Swahili.
28
+
29
+ A farmer in Nakuru doesn't know that maize prices in Nairobi are 40% higher that week. Traders know. Farmers don't. This information asymmetry is one of the most costly structural disadvantages facing smallholder farmers. soko-mcp closes it.
30
+
31
+ ## The Structural Problem
32
+
33
+ In mature commodity markets, price discovery is instantaneous โ€” futures markets, satellite price tickers, and SMS alerts exist for every major exchange. A grain elevator in Iowa checks live CME prices before making any offer.
34
+
35
+ In Kenya, most smallholder farmers receive the price the trader offers, with no independent benchmark to compare against. The result: systematic underpricing at harvest, systematic overpricing at planting.
36
+
37
+ **Information asymmetry is a tax on the poor.**
38
+
39
+ ## Tools
40
+
41
+ | Tool | What it does |
42
+ |------|-------------|
43
+ | `commodity_price_query` | Current price for commodity at a specific market |
44
+ | `regional_price_comparison` | Compare prices across all major Kenya markets |
45
+ | `price_trend_analysis` | 6-month history + 3-month forecast with seasonal model |
46
+ | `sell_hold_decision` | Optimal sell/hold timing given storage costs and price trend |
47
+ | `market_overview` | Multi-commodity price snapshot for a market |
48
+
49
+ ## Quick Start
50
+
51
+ ```bash
52
+ pip install soko-mcp # coming soon to PyPI
53
+ soko-mcp # starts on stdio
54
+ ```
55
+
56
+ ## Example Queries for Claude
57
+
58
+ ```
59
+ "What is the current Nairobi maize price?"
60
+ "Should I sell my 50 bags of beans now or wait 2 months?"
61
+ "Compare potato prices across all Kenya markets"
62
+ "Give me a price trend for avocados in Nakuru for the next 3 months"
63
+ ```
64
+
65
+ ## Research Basis
66
+
67
+ - EAGC East Africa Regional Market Monitor
68
+ - World Bank "Information and Communication Technology and Agricultural Markets" (2016)
69
+ - Suri & Jack "Mobile Phones and Agricultural Performance" (2016)
70
+
71
+ โš ๏ธ DEMO data โ€” synthetic seasonal model. Verify at eagc.org, kalro.org, or local market boards.
72
+
73
+ ---
74
+ *ยฉ 2026 Gabriel Mahia / AI Kung Fu LLC ยท MIT License*
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/soko_mcp/__init__.py
5
+ src/soko_mcp/main.py
6
+ src/soko_mcp.egg-info/PKG-INFO
7
+ src/soko_mcp.egg-info/SOURCES.txt
8
+ src/soko_mcp.egg-info/dependency_links.txt
9
+ src/soko_mcp.egg-info/entry_points.txt
10
+ src/soko_mcp.egg-info/requires.txt
11
+ src/soko_mcp.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ soko-mcp = soko_mcp.main:main
@@ -0,0 +1 @@
1
+ fastmcp>=2.0.0
@@ -0,0 +1 @@
1
+ soko_mcp