Skip to content

Conversation

Haniehz1
Copy link
Contributor

@Haniehz1 Haniehz1 commented Sep 16, 2025

Main changes:

Added @app.tool
async def analyze_stock(company_name: str = "Apple") -> str:
"""Analyze a stock and generate a comprehensive financial report."""

  • Uses @app.tool decorator to expose as MCP tool
  • Function-based with parameters
  • Returns string results

os.makedirs(OUTPUT_DIR, exist_ok=True) # COMMENTED OUT

context = app.context # Not needed when filesystem is commented out

  • File operations commented out
  • No filesystem server configuration
  • Returns text output directly

server_names=["fetch"] # All agents use fetch only

  • Only uses fetch server
  • No Google Search dependency
  • No filesystem server => commented out

Output Handling:
return f"✅ Stock Analysis Complete for {company_name}!\n\n" + "="*60 + "\n" + result

  • Returns formatted string with results
  • No file verification

Error Handling
return f"💥 Error during workflow execution: {str(e)}"

  • Returns error as string

Summary by CodeRabbit

  • New Features

    • Introduced analyze_stock tool that generates an institutional-style stock report as a single text output.
    • Added an orchestrated workflow combining research, evaluation, analysis, and report formatting.
    • CLI support: pass a company name and print the resulting report.
  • Improvements

    • Elevated data quality standards to deliver more reliable reports.
    • Removed file outputs and external search server dependency for a faster, cleaner flow.
  • Refactor

    • App renamed to unified_stock_analyzer.

Main changes: 

  @app.tool
  async def analyze_stock(company_name: str = "Apple") -> str:
      """Analyze a stock and generate a comprehensive financial report."""
  - Uses @app.tool decorator to expose as MCP tool
  - Function-based with parameters
  - Returns string results

  # os.makedirs(OUTPUT_DIR, exist_ok=True)  # COMMENTED OUT
  # context = app.context  # Not needed when filesystem is commented out
  - File operations commented out
  - No filesystem server configuration
  - Returns text output directly

server_names=["fetch"]  # All agents use fetch only
  - Only uses fetch server
  - No Google Search dependency
  - No filesystem server => commented out 

Output Handling: 
  return f"✅ Stock Analysis Complete for {company_name}!\n\n" + "="*60 + "\n" + result
  - Returns formatted string with results
  - No file verification

Error Handling
  return f"💥 Error during workflow execution: {str(e)}"
  - Returns error as string
Copy link

coderabbitai bot commented Sep 16, 2025

Walkthrough

Replaces the previous stock analyzer with an orchestrated workflow using an Orchestrator and AugmentedLLM. Adds an async tool analyze_stock that orchestrates research, quality evaluation, analysis, and report writing, returning a string. Elevates quality threshold to EXCELLENT, updates MCP app name, and introduces a simple CLI entry that prints the final report.

Changes

Cohort / File(s) Summary
Workflow orchestration refactor
examples/usecases/mcp_financial_analyzer/main.py
Replaced prior agent flow with Orchestrator + OpenAIAugmentedLLM; added agents: research_agent, research_evaluator, analyst_agent, report_writer; added research_quality_controller using EvaluatorOptimizerLLM with min_rating=EXCELLENT; introduced public async tool analyze_stock(company_name) -> str; switched app name to unified_stock_analyzer; removed file I/O and server checks; added CLI entry to print returned report.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CLI as CLI (main)
  participant App as MCPApp (unified_stock_analyzer)
  participant Tool as tool analyze_stock(company_name)
  participant Orc as Orchestrator
  participant LLM as OpenAIAugmentedLLM
  participant RA as research_agent
  participant QC as research_quality_controller
  participant EV as research_evaluator
  participant FA as analyst_agent
  participant RW as report_writer

  User->>CLI: Provide company name
  CLI->>App: Initialize & invoke tool
  App->>Tool: analyze_stock(company_name)
  Tool->>Orc: orchestrate(company_name)
  Orc->>RA: Gather data
  RA-->>Orc: Research results
  Orc->>QC: Evaluate quality (min EXCELLENT)
  QC-->>Orc: Pass/Fail rating
  alt Quality sufficient
    Orc->>EV: Assess and summarize quality
    EV-->>Orc: Quality assessment
    Orc->>FA: Perform financial analysis
    FA-->>Orc: Analysis insights
    Orc->>RW: Format institutional report
    RW-->>Orc: Final report (string)
    Orc-->>Tool: Return report
    Tool-->>App: Report string
    App-->>CLI: Report string
    CLI-->>User: Print report
  else Quality insufficient
    note over Orc,RA: Retry/augment research as configured
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Feat/update financial analyzer main.py #318 — Modifies the same financial analyzer, refactoring agent orchestration, MCPApp naming, and EvaluatorOptimizerLLM min_rating, indicating overlapping code paths and intent.

Suggested reviewers

  • saqadri
  • andrew-lastmile

Poem

I thump my paws on trading floors,
Orchestrate the data roars—
Research hops, quality stops,
Analyst chews on earnings crops.
Report is plated, crisp and bright,
A carrot-gilded market insight. 🥕📈

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly states the primary intent — making the basic Slack agent example compatible with MCP cloud — and matches the PR objectives (exposing an @app.tool analyze_stock, removing filesystem dependencies, and restricting servers to fetch-only), so it accurately reflects the main change.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch Haniehz1-patch-12-1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

output_file = f"{COMPANY_NAME.lower().replace(' ', '_')}_report_{timestamp}.md"
output_path = os.path.join(OUTPUT_DIR, output_file)
# output_file = f"{company_name.lower().replace(' ', '_')}_report_{timestamp}.md"
output_path = os.path.join(OUTPUT_DIR, f"{company_name.lower().replace(' ', '_')}_report_{timestamp}.md")
Copy link
Contributor

Choose a reason for hiding this comment

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

Variable OUTPUT_DIR is used but never defined in the new code structure. The original OUTPUT_DIR definition was removed when converting from main() to @app.tool function, but line 38 still references it. This will cause a NameError at runtime. Either define OUTPUT_DIR as a constant or remove this line entirely since file operations are commented out.

Suggested change
output_path = os.path.join(OUTPUT_DIR, f"{company_name.lower().replace(' ', '_')}_report_{timestamp}.md")
# output_path = os.path.join(OUTPUT_DIR, f"{company_name.lower().replace(' ', '_')}_report_{timestamp}.md")

Spotted by Diamond

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

logger = analyzer_app.logger
# Access the running app context directly
# context = app.context # Not needed when filesystem is commented out
logger = app.logger
Copy link
Contributor

Choose a reason for hiding this comment

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

Accessing app.logger outside of the app context will fail. The analyze_stock function is decorated with @app.tool but tries to access app.logger directly without being inside an async with app.run() context. This will cause an AttributeError or return None/undefined logger. The logger should be accessed through the app context when the tool is actually executed.

Suggested change
logger = app.logger
# Logger will be accessed within functions when needed

Spotted by Diamond

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

- Include source URLs for verification
- Note data timestamps/dates
- If any section is missing data, explicitly state what couldn't be found. """,
server_names=["fetch"],
Copy link
Contributor

Choose a reason for hiding this comment

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

Server configuration mismatch: research_agent uses server_names=["fetch"] but its instructions contain multiple references to Google Search functionality (lines 63, 68, 73, 78 with search queries like 'Search: "{company_name} stock price today current"'). The agent expects to perform Google searches but only has access to the fetch server, which will cause tool execution failures when it tries to search.

Suggested change
server_names=["fetch"],
server_names=["fetch", "search"],

Spotted by Diamond

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (8)
examples/usecases/mcp_financial_analyzer/main.py (8)

22-26: Remove dead file-output code and unused constants

OUTPUT_DIR, COMPANY_NAME, MAX_ITERATIONS, timestamp, and output_path are unused after switching to pure string output.

Apply:

@@
-# Configuration values
-OUTPUT_DIR = "company_reports"
-COMPANY_NAME = "Apple" if len(sys.argv) <= 1 else sys.argv[1]
-MAX_ITERATIONS = 3
+# Configuration values (filesystem disabled; keep config minimal)
+MODEL_NAME = os.getenv("MCP_OPENAI_MODEL", "gpt-4o")
@@
-    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
-    # output_file = f"{company_name.lower().replace(' ', '_')}_report_{timestamp}.md"
-    output_path = os.path.join(OUTPUT_DIR, f"{company_name.lower().replace(' ', '_')}_report_{timestamp}.md")
+    # (no file output variables needed)

Also applies to: 36-39


44-50: Delete commented-out filesystem server code

These comments are now noise and can mislead future readers.

Apply:

-    # Configure filesystem server to use current directory (COMMENTED OUT - NOW PRINTING RESULTS)
-    # if "filesystem" in context.config.mcp.servers:
-    #     context.config.mcp.servers["filesystem"].args.extend([os.getcwd()])
-    #     logger.info("Filesystem server configured")
-    # else:
-    #     logger.warning("Filesystem server not configured - report saving may fail")

31-34: Sanitize user-provided company name to reduce prompt-injection surface

company_name is interpolated into multiple prompts as instructions. Sanitize/limit length to prevent adversarial input shaping agent behavior.

Apply:

@@
 async def analyze_stock(company_name: str = "Apple") -> str:
     """Analyze a stock and generate a comprehensive financial report."""
+    # Sanitize input to reduce prompt-injection and template breakage
+    import re
+    company_name = (re.sub(r'[^A-Za-z0-9 .,&\-()/#:+%]', '', company_name) or "Apple")[:120].strip()

248-257: Hard-coded “EST” can be wrong; use timezone-aware timestamp

Use zoneinfo to render the local New York market time correctly (EST/EDT).

Apply:

@@
-from datetime import datetime
+from datetime import datetime
+from zoneinfo import ZoneInfo
@@
-        **Report Date:** {datetime.now().strftime('%B %d, %Y at %I:%M %p EST')}
+        **Report Date:** {datetime.now(ZoneInfo('America/New_York')).strftime('%B %d, %Y at %I:%M %p %Z')}

185-191: Make quality threshold and refinement budget configurable

min_rating=EXCELLENT with default max_refinements=3 can be costly/slow. Parameterize via env to tune for cloud costs/latency.

Apply:

@@
-    research_quality_controller = EvaluatorOptimizerLLM(
+    research_quality_controller = EvaluatorOptimizerLLM(
         optimizer=research_agent,
         evaluator=research_evaluator,
         llm_factory=OpenAIAugmentedLLM,
-        min_rating=QualityRating.EXCELLENT,
+        min_rating=QualityRating[os.getenv("MCP_QUALITY_MIN_RATING", "EXCELLENT")],
+        max_refinements=int(os.getenv("MCP_RESEARCH_MAX_REFINEMENTS", "3")),
     )

409-411: Parameterize the model via env var

Avoid hard-coding the model name; makes cloud deployment configurable.

Apply:

-        result = await orchestrator.generate_str(
-            message=task, request_params=RequestParams(model="gpt-4o")
-        )
+        result = await orchestrator.generate_str(
+            message=task, request_params=RequestParams(model=os.getenv("MCP_OPENAI_MODEL", "gpt-4o"))
+        )

424-426: Don’t leak exception details to end users

Return a generic error and log the stack trace server-side.

Apply:

-    except Exception as e:
-        logger.error(f"Error during workflow execution: {str(e)}")
-        return f"💥 Error during workflow execution: {str(e)}"
+    except Exception:
+        logger.exception("Error during workflow execution")
+        return "💥 Error during workflow execution. Please try again later."

357-363: Cap report length to control token usage/cost

1200–1800 words may exceed practical budgets. Consider 800–1200 words or make this configurable via env.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78dd3a0 and 5405983.

📒 Files selected for processing (1)
  • examples/usecases/mcp_financial_analyzer/main.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
examples/usecases/mcp_financial_analyzer/main.py (3)
src/mcp_agent/app.py (3)
  • tool (717-769)
  • logger (193-210)
  • run (397-420)
src/mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py (2)
  • EvaluatorOptimizerLLM (49-478)
  • QualityRating (25-31)
src/mcp_agent/workflows/orchestrator/orchestrator.py (1)
  • Orchestrator (116-716)
🔇 Additional comments (1)
examples/usecases/mcp_financial_analyzer/main.py (1)

372-385: Orchestrator wiring looks solid

Passing EvaluatorOptimizerLLM directly in available_agents and using plan_type="full" is consistent with the Orchestrator API.

Comment on lines +31 to +34
@app.tool
async def analyze_stock(company_name: str = "Apple") -> str:
"""Analyze a stock and generate a comprehensive financial report."""
# Create output directory and set up file paths (COMMENTED OUT - NOW PRINTING RESULTS)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

MCP tool exposure + string output is the right move for MCP Cloud

Registering analyze_stock as an MCP tool that returns a string (no filesystem writes) aligns with cloud constraints.

Please confirm the @app.tool decorator supports async functions in your version. To verify:


🏁 Script executed:

#!/bin/bash
# Check decorator docs/impl to ensure async functions are supported
rg -n -C3 "def tool\(" src/mcp_agent/app.py
rg -n -C3 "_create_workflow_from_function" src/mcp_agent/app.py

Length of output: 885


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="src/mcp_agent/app.py"
echo "=== Showing lines 500-840 of $file ==="
nl -ba "$file" | sed -n '500,840p' || true

echo
echo "=== Search for async/await/inspect helpers in src ==="
rg -n --hidden --no-ignore -S "async def|inspect.iscoroutinefunction|inspect.isawaitable|isawaitable" src || true

echo
echo "=== Show surrounding lines where `def tool(` and `_create_workflow_from_function` were matched ==="
rg -n -C8 --hidden --no-ignore "def tool\(|def _create_workflow_from_function\(" src/mcp_agent/app.py || true

Length of output: 57554


@app.tool is synchronous — make the tool function sync or register it as an async-capable workflow

src/mcp_agent/app.py defines the tool decorator as a synchronous tool (see def tool at src/mcp_agent/app.py ~717); examples/usecases/mcp_financial_analyzer/main.py currently registers async def analyze_stock — convert it to a regular def analyze_stock(...) -> str (remove async) or register it via a workflow/adapter that supports async.

🤖 Prompt for AI Agents
In examples/usecases/mcp_financial_analyzer/main.py around lines 31 to 34, the
function analyze_stock is declared async but app.tool is a synchronous tool
decorator; change analyze_stock to a synchronous function by removing the async
keyword (def analyze_stock(company_name: str = "Apple") -> str), ensure any
awaited calls inside are converted to synchronous equivalents or run via
asyncio.run/loop if necessary, and return a plain str; alternatively, if you
need true async behavior, register the function through the async-capable
workflow/adapter API instead of using @app.tool so the decorator and function
capabilities match.

Comment on lines +53 to +60
# Research agent: Collects data using Google Search
research_agent = Agent(
name="search_finder",
instruction=f"""You are a comprehensive financial data collector for {company_name}.
Your job is to gather ALL required financial information using Google Search and fetch tools.
**REQUIRED DATA TO COLLECT:**
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Prompt still requires “Google Search” while only ‘fetch’ is allowed

With server_names=["fetch"] and the PR goal to drop Google Search, instructing the agent to use “Google Search” will lead to failed tool calls or brittle scraping of search pages.

Apply minimally:

-        Your job is to gather ALL required financial information using Google Search and fetch tools.
+        Your job is to gather ALL required financial information using the HTTP fetch tool only (no search engine APIs).
+        Prefer reputable sources such as SEC EDGAR, the company's Investor Relations site, Yahoo Finance, Nasdaq, Reuters, and WSJ. Include direct source URLs and timestamps.
@@
-           Search: "{company_name} stock price today current"
-           Search: "{company_name} trading volume market data"
+           Find and fetch: a real-time/last close quote page (e.g., Yahoo Finance) showing price, % change, volume, 52-week range
@@
-           Search: "{company_name} latest quarterly earnings results"
-           Search: "{company_name} earnings vs estimates beat miss"
+           Find and fetch: latest quarterly earnings release and consensus summary (company IR/press release; Yahoo Finance earnings page)
@@
-           Search: "{company_name} financial news latest week"
-           Search: "{company_name} analyst ratings upgrade downgrade"
+           Find and fetch: 3–5 recent news items from credible outlets with dates and sources; include any recent analyst actions
@@
-           Search: "{company_name} PE ratio market cap financial metrics"
+           Find and fetch: key valuation metrics from a reliable quote/overview page

Ensure the MCP Cloud environment has a configured ‘fetch’ server. Quick check:

Also applies to: 63-80


🏁 Script executed:

#!/bin/bash
# Look for MCP server configuration mentioning 'fetch'
rg -n -C2 '"servers"\s*:\s*\{' -g '!**/node_modules/**' -S
rg -n -C2 '"fetch"' -S

Length of output: 52426


Replace "Google Search" with fetch-only instructions in the financial research agent prompt

examples/usecases/mcp_financial_analyzer/main.py — lines 53-60 (also applies to 63-80). Verified the repo already configures a 'fetch' server (e.g., src/mcp_agent/data/templates/basic_agent.py, src/mcp_agent/cli/commands/config.py, tests reference "fetch"); update the prompt to use the HTTP fetch tool only.

-        Your job is to gather ALL required financial information using Google Search and fetch tools.
+        Your job is to gather ALL required financial information using the HTTP fetch tool only (no search engine APIs).
+        Prefer reputable sources such as SEC EDGAR, the company's Investor Relations site, Yahoo Finance, Nasdaq, Reuters, and WSJ. Include direct source URLs and timestamps.
@@
-           Search: "{company_name} stock price today current"
-           Search: "{company_name} trading volume market data"
+           Find and fetch: a real-time/last close quote page (e.g., Yahoo Finance) showing price, % change, volume, 52-week range
@@
-           Search: "{company_name} latest quarterly earnings results"
-           Search: "{company_name} earnings vs estimates beat miss"
+           Find and fetch: latest quarterly earnings release and consensus summary (company IR/press release; Yahoo Finance earnings page)
@@
-           Search: "{company_name} financial news latest week"
-           Search: "{company_name} analyst ratings upgrade downgrade"
+           Find and fetch: 3–5 recent news items from credible outlets with dates and sources; include any recent analyst actions
@@
-           Search: "{company_name} PE ratio market cap financial metrics"
+           Find and fetch: key valuation metrics from a reliable quote/overview page
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Research agent: Collects data using Google Search
research_agent = Agent(
name="search_finder",
instruction=f"""You are a comprehensive financial data collector for {company_name}.
Your job is to gather ALL required financial information using Google Search and fetch tools.
**REQUIRED DATA TO COLLECT:**
# Research agent: Collects data using Google Search
research_agent = Agent(
name="search_finder",
instruction=f"""You are a comprehensive financial data collector for {company_name}.
Your job is to gather ALL required financial information using the HTTP fetch tool only (no search engine APIs).
Prefer reputable sources such as SEC EDGAR, the company's Investor Relations site, Yahoo Finance, Nasdaq, Reuters, and WSJ. Include direct source URLs and timestamps.
**REQUIRED DATA TO COLLECT:**
🤖 Prompt for AI Agents
In examples/usecases/mcp_financial_analyzer/main.py around lines 53-60 (and
similarly lines 63-80), the research agent prompt still instructs "Google
Search" — change the prompt to instruct the agent to use only the configured
HTTP fetch tool (fetch server) instead of Google Search; reword the instruction
to explicitly say use the fetch tool/HTTP endpoints to retrieve pages, follow
links, and collect the required financial data, and remove any references to
Google Search so the prompt aligns with the repo's fetch-only configuration.

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.

1 participant