Skip to content

Mcp restaurant agent#32

Open
shahchayan9 wants to merge 4 commits into
mainfrom
mcp-restaurant-agent
Open

Mcp restaurant agent#32
shahchayan9 wants to merge 4 commits into
mainfrom
mcp-restaurant-agent

Conversation

@shahchayan9
Copy link
Copy Markdown
Contributor

@shahchayan9 shahchayan9 commented Apr 21, 2026

Summary

This PR adds a new MCP-based restaurant recommendation agent example.

The agent takes natural language queries, uses MCP tools backed by Google Places API to fetch real-time restaurant data, and returns structured recommendations. It also supports a follow-up flow to fetch detailed restaurant information (address, hours, contact, etc.) and includes Yelp enrichment for better user experience.


Type of Change

  • New agent example
  • Bug fix
  • Documentation update
  • Refactor / cleanup
  • Other

Checklist

  • I have starred this repository.
  • I ran ruff check ..
  • I ran ruff format ..
  • I added/updated README.md for changed example(s).
  • I added .env.example if environment variables are required.
  • I added demo image/GIF (if applicable).
  • I added agent profile link (if applicable).
  • I updated CHANGELOG.md (required for non-doc changes).
  • I verified paths/commands used in docs.

Related Issue

N/A


Notes for Reviewers

  • This example demonstrates how to integrate MCP with a Fetch.ai uAgent to build a tool-driven, real-time recommendation system.
  • Uses Google Places API for restaurant discovery and optional Yelp API for enrichment (menu links, additional context).
  • Currently optimized for demo use; some filtering edge cases (Google–Yelp entity matching) can be further improved.
  • Stripe/payment-related code was intentionally removed to keep the example focused and runnable out-of-the-box.

Comment thread mcp-agents/mcp-booking-main/src/fetch_uagent.py Outdated
@gautammanak1 gautammanak1 self-requested a review May 3, 2026 18:06
if DOTENV_PATH.exists():
dot = dotenv_values(DOTENV_PATH)
dv = dot.get(key)
if dv is not None and str(dv).strip() != "":
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mcp-agents/mcp-booking-main/src/stripe_checkout.py:34 — Syntax error (extra closing parenthesis)

if dv is not None and str(dv).strip() != "":
    return str(dv).strip())  # ← Extra closing parenthesis

This line will cause a SyntaxError when the file is imported. Remove the extra parenthesis:

Suggested change
if dv is not None and str(dv).strip() != "":
if dv is not None and str(dv).strip() != "":
return str(dv).strip()

"""
if not is_configured():
return None
s = _get_stripe()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mcp-agents/mcp-booking-main/src/stripe_checkout.py:169 — Syntax error (missing closing parenthesis)

"amount_cents": int(c["amount_cents"),  # ← Missing closing parenthesis

This line will cause a SyntaxError. Add the missing parenthesis:

Suggested change
s = _get_stripe()
"amount_cents": int(c["amount_cents"]),

),
)
return

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mcp-agents/mcp-booking-main/src/fetch_uagent.py:334true should be True (Python is case-sensitive)

if not state.paid_unlocked and state.pending_checkout_session_id:
    if verify_checkout_session_paid(state.pending_checkout_session_id):
        state.paid_unlocked = true  # ← Should be True

Python uses True/False, not true/false. Also, verify_checkout_session_paid is a synchronous function that makes a network call, which blocks the event loop.

Fix: Use True and consider making the verification async:

Suggested change
if not state.paid_unlocked and state.pending_checkout_session_id:
if await verify_checkout_session_paid(state.pending_checkout_session_id):
state.paid_unlocked = True

CompletePayment,
Funds,
RejectPayment,
RequestPayment,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mcp-agents/mcp-booking-main/src/fetch_uagent.py:40 — Hardcoded default seed is a security risk

DEFAULT_SEED = os.getenv(
    "FETCH_UAGENT_SEED",
    "replace-this-with-your-own-strong-seed-phrase-please",
)

The seed deterministically generates the agent's address and wallet. Anyone with the seed can impersonate the agent and drain its FET. The default placeholder could accidentally be used in production.

Fix: Remove the default and require the environment variable:

Suggested change
RequestPayment,
DEFAULT_SEED = os.getenv("FETCH_UAGENT_SEED")
if not DEFAULT_SEED:
raise ValueError("FETCH_UAGENT_SEED environment variable is required")


query = _get_chat_user_text(msg)
if not query:
await ctx.send(sender, _chat_text_message("Please send a text query."))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mcp-agents/mcp-booking-main/src/fetch_uagent.py:368 — Same issue: true should be True

if not state.paid_unlocked and state.pending_checkout_session_id:
        if verify_checkout_session_paid(state.pending_checkout_session_id):
            state.paid_unlocked = true  # ← Should be True

Same as line 334 - use True instead of true.

Suggested change
await ctx.send(sender, _chat_text_message("Please send a text query."))
state.paid_unlocked = True

f"{c['success_url']}"
f"?session_id={{CHECKOUT_SESSION_ID}}"
f"&chat_session_id={chat_session_id}"
f"&user={user_address}"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mcp-agents/mcp-booking-main/src/stripe_checkout.py:112,193 — Bare except swallows errors

except Exception:
    return None

These bare except clauses silently swallow all errors, making debugging difficult. At minimum, log the error:

Suggested change
f"&user={user_address}"
except Exception as e:
import logging
logging.error(f"Stripe checkout session creation failed: {e}")
return None

await ctx.send(sender, ResponseMessage(text="Missing request text."))
return

state = _gate_state(sender)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mcp-agents/mcp-booking-main/src/fetch_uagent.py:287 — Dead code (duplicate assignment)

def _gate_state(sender: str) -> UserGateState:
    key = _paywall_state_key(sender)
    state = _gate_state_by_sender.get(key)
    if state is None:
        state = UserGateState()
        _gate_state_by_sender[key] = state
    state = state  # ← This line does nothing
    return state

Line 287 assigns state to itself. This is harmless but suggests leftover refactoring code. Remove it.

Suggested change
state = _gate_state(sender)
if state is None:
state = UserGateState()
_gate_state_by_sender[key] = state
return state

@gautammanak1
Copy link
Copy Markdown
Collaborator

Review: MCP Restaurant Agent - PR #32

Summary

This PR adds a comprehensive MCP-based restaurant recommendation agent with:

  • MCP server exposing a search_restaurants tool backed by Google Places API
  • Natural language query parsing with cuisine/mood/event/price extraction
  • AI-powered restaurant scoring and recommendation logic
  • Yelp integration for menu/profile enrichment (optional)
  • uAgent wrapper with chat protocol and payment protocol (Stripe) support
  • Good test coverage for the recommendation service

Overall risk: Medium - The code is well-structured and follows best practices, but there are a few security and correctness issues that should be addressed before merging.


What I Liked

  • Clean MCP integration: The index.ts server properly implements the MCP protocol with session management, health checks, and proper error handling.
  • Comprehensive recommendation logic: The scoring algorithm in restaurantRecommendationService.ts thoughtfully balances rating, review count, cuisine match, event suitability, and mood match.
  • Good test coverage: The tests cover parallel processing, scoring algorithms, edge cases, and performance benchmarks.
  • Proper protocol usage: The uAgent correctly implements the chat protocol (with acknowledgements) and payment protocol (with correct role="seller").
  • Graceful degradation: Yelp enrichment is optional and the code handles missing API keys gracefully.

Security 🔒

mcp-agents/mcp-booking-main/src/fetch_uagent.py:40 — Hardcoded default seed is a security risk

DEFAULT_SEED = os.getenv(
    "FETCH_UAGENT_SEED",
    "replace-this-with-your-own-strong-seed-phrase-please",
)

Issue: The default seed value is a placeholder that could accidentally be used in production. The seed deterministically generates the agent's address and wallet, so anyone with the seed can impersonate the agent and drain its FET.

Fix: Remove the default and require the environment variable:

DEFAULT_SEED = os.getenv("FETCH_UAGENT_SEED")
if not DEFAULT_SEED:
    raise ValueError("FETCH_UAGENT_SEED environment variable is required")

mcp-agents/mcp-booking-main/src/fetch_uagent.py:287 — Duplicate assignment (dead code)

def _gate_state(sender: str) -> UserGateState:
    key = _paywall_state_key(sender)
    state = _gate_state_by_sender.get(key)
    if state is None:
        state = UserGateState()
        _gate_state_by_sender[key] = state
    state = state  # ← This line does nothing
    return state

Issue: Line 287 assigns state to itself. This is harmless but suggests leftover refactoring code.

Fix: Remove the redundant line.

mcp-agents/mcp-booking-main/src/fetch_uagent.py:334 — Missing await on async call

if not state.paid_unlocked and state.pending_checkout_session_id:
    if verify_checkout_session_paid(state.pending_checkout_session_id):
        state.paid_unlocked = true  # ← Should be True (Python is case-sensitive)

Issue: Two problems:

  1. true is not a valid Python value (should be True)
  2. verify_checkout_session_paid is a synchronous function but it makes a network call to Stripe - this blocks the event loop

Fix: Make the verification async and use True:

if not state.paid_unlocked and state.pending_checkout_session_id:
    if await verify_checkout_session_paid(state.pending_checkout_session_id):
        state.paid_unlocked = True

You'll need to update stripe_checkout.py to make verify_checkout_session_paid async:

async def verify_checkout_session_paid(checkout_session_id: str) -> bool:
    # ... existing code ...
    # Wrap the blocking stripe call in asyncio.to_thread
    return await asyncio.to_thread(_verify_sync, checkout_session_id)

mcp-agents/mcp-booking-main/src/fetch_uagent.py:368 — Same issue with true vs True

Line 368 also uses true instead of True.


Correctness & Logic 🐛

mcp-agents/mcp-booking-main/src/stripe_checkout.py:34 — Syntax error

if dv is not None and str(dv).strip() != "":
    return str(dv).strip())  # ← Extra closing parenthesis

Issue: Line 34 has an extra closing parenthesis that will cause a syntax error.

Fix:

if dv is not None and str(dv).strip() != "":
    return str(dv).strip()

mcp-agents/mcp-booking-main/src/stripe_checkout.py:169 — Syntax error

"amount_cents": int(c["amount_cents"),  # ← Missing closing parenthesis

Issue: Line 169 is missing a closing parenthesis.

Fix:

"amount_cents": int(c["amount_cents"]),

mcp-agents/mcp-booking-main/src/stripe_checkout.py:193 — Bare except swallows errors

except Exception:
    return None

Issue: Lines 112 and 193 have bare except Exception clauses that silently swallow all errors. This makes debugging difficult and could hide real issues.

Fix: At minimum log the error:

except Exception as e:
    import logging
    logging.error(f"Stripe checkout failed: {e}")
    return None

mcp-agents/mcp-booking-main/src/agent/mcpSearchClient.ts:35-38 — Use strict equality

if (!data || !Array.isArray(data.recommendations)) {
  throw new Error('Unexpected MCP response: missing recommendations array');
}

The code uses ! which is fine, but the static analysis flagged potential type coercion issues. The code is actually correct here, but consider using more explicit type guards if you want to be extra defensive.


Performance ⚡

mcp-agents/mcp-booking-main/src/services/restaurantRecommendationService.ts:28-39 — Good parallel processing

The recommendation service correctly uses Promise.all for parallel processing of independent calculations. This is well-implemented.

mcp-agents/mcp-booking-main/src/index.ts:207-213 — Parallel Yelp enrichment

const enriched = await Promise.all(
  recommendations.map(async rec => {
    const yelp = this.yelpService.isEnabled()
      ? await this.yelpService.enrichRestaurant(rec.restaurant)
      : {};
    return { rec, yelp };
  })
);

Good use of Promise.all for parallel Yelp enrichment. However, consider adding a timeout to prevent slow Yelp responses from blocking the entire request:

const enriched = await Promise.all(
  recommendations.map(async rec => {
    if (!this.yelpService.isEnabled()) return { rec, yelp: {} };
    const yelp = await Promise.race([
      this.yelpService.enrichRestaurant(rec.restaurant),
      new Promise<{}>(resolve => setTimeout(() => resolve({}), 5000)),
    ]);
    return { rec, yelp };
  })
);

Code Quality 🧹

Long lines

Several lines exceed 120 characters. Consider breaking them up for readability:

  • src/index.ts:91 (137 chars)
  • src/index.ts:103 (123 chars)
  • src/index.ts:133 (235 chars)
  • src/index.ts:139 (194 chars)
  • src/index.ts:179 (129 chars)

Console.log statements

The index.ts file has several console.log statements for startup information. Consider using a proper logging library (like pino or winston) for production use, or gate them behind a DEBUG environment variable.


Tests ✅

The test suite is comprehensive and covers:

  • Parallel processing performance
  • Scoring algorithm correctness
  • Event and mood matching
  • Edge cases (empty lists, identical scores, missing criteria)
  • Performance benchmarks

The tests look good! One small suggestion: add a test for the error path when the Stripe checkout fails.


Documentation 📚

The PR description mentions that Stripe/payment code was "intentionally removed to keep the example focused and runnable out-of-the-box." However:

  • stripe_checkout.py is present and fully implemented
  • fetch_uagent.py imports and uses the Stripe functions
  • The payment protocol is included

This is a discrepancy. Either:

  1. Remove the Stripe code entirely, or
  2. Update the PR description to reflect that Stripe is included but optional

Questions for the Author

  1. Stripe inclusion: The PR description says Stripe was "intentionally removed" but the code is still there. Which is correct?

  2. Blocking Stripe calls: Should verify_checkout_session_paid be made async to avoid blocking the event loop?

  3. Error handling: Would you like me to suggest a more structured error handling approach for the Stripe integration?


Blocking Issues (Must Fix Before Merge)

  1. Syntax errors in stripe_checkout.py (lines 34, 169)
  2. Hardcoded default seed in fetch_uagent.py (line 40)
  3. true vs True typos in fetch_uagent.py (lines 334, 368)

Nice-to-Have

  1. Add timeouts to Yelp enrichment calls
  2. Make Stripe verification async
  3. Add logging for bare except clauses
  4. Consider removing or documenting the Stripe code based on the PR description

@gautammanak1
Copy link
Copy Markdown
Collaborator

Remove extra files like render, GitHub workflows, and templates, as they are already included across all repositories.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants