This document captures learnings, patterns, and common issues when using the MCP server tools.
Issue: Calling samplingHandler.handleSampling() but method is createMessage()
Fix: Changed to samplingHandler.createMessage({ params: p }, ex)
Status: Code fixed, requires server restart
Issue: reportId coming through as undefined
Fix: Enhanced parameter normalization to handle:
{reportId: "2"}- standard{reportId: 2}- number (auto-converted to string){id: "2"}- alternate key{report_id: "2"}- snake_case"2"- raw string input Status: Code fixed, requires server restart
The server's normalizeParamsForTool() function converts loose inputs to structured params:
// All of these work for calc:
{"expr": "2+2"} // Structured (preferred)
"2+2" // Raw string
{"random_string": "2+2"} // Legacy format// Auto-detects SQL mode:
{"sql": "SELECT * FROM reports"} // mode becomes "sql"
"SELECT * FROM reports" // also detected as SQL
// Auto-detects index mode:
{"query": "find research"} // mode becomes "index"
"find research" // also index mode// These are equivalent:
{"query": "topic", "costPreference": "low"}
{"q": "topic", "cost": "low"}- Gotcha: Without
queryparameter, runs with empty/default query - Solution: Always provide explicit
query - Gotcha:
async: truereturns job_id,async: falsestreams - Pattern: Use async for >30s expected research
- Gotcha: Jobs have 1-hour TTL, then expire from memory
- Pattern: Extract report ID immediately when job completes
- Pattern: Use
since_event_idfor incremental polling
- Gotcha: Requires string reportId (now auto-converted)
- Gotcha:
mode: "summary"requires semantic embedding - Pattern: Use
mode: "full"for complete content
- Gotcha: Requires
qparameter (not optional) - Pattern: Use
scope: "reports"for research-only results - Pattern:
kdefaults to 10, increase for broader search
- Gotcha: Only SELECT statements allowed
- Gotcha: Params use PostgreSQL $1, $2 placeholders
- Pattern: Use
explain: truefor natural language summary
- Gotcha: Requires
exprparameter explicitly - Gotcha: Error message "Invalid characters" when expr missing
- Supported: +, -, *, /, ^, (), decimals
- Gotcha: Format parameter is optional
- Pattern:
"iso","rfc","epoch","unix"(same as epoch)
research → job_id
↓
job_status (poll) → status: running/completed/failed
↓
When completed: Parse result for "Report ID: X"
↓
get_report {reportId: "X"}
Gotcha: Report ID is in the result message string, needs parsing
search {q: "topic"} → Check existing knowledge
↓
If found: get_report for details
If not found: conduct_research for new synthesis
Pattern: Avoid redundant research by checking KB first
The server attempts to coerce types:
| Parameter | Input | Coerced To |
|---|---|---|
| reportId | 2 (number) |
"2" (string) |
| limit | "10" (string) |
10 (number) |
| async | "true" (string) |
true (boolean) |
| k | "5" (string) |
5 (number) |
// Error: "Job unknown: Not found or invalid job ID"
// Recovery:
1. Check task_list for valid job IDs
2. Jobs expire after 1 hour
3. Use correct job_id format: "job_xxx_yyy"// Error: "Report ID X not found"
// Recovery:
1. Check history for valid report IDs
2. Ensure reportId is string type
3. Reports persist in DB (don't expire like jobs)// Error: "Query must not be empty"
// Recovery:
1. Always provide explicit query parameter
2. Don't rely on defaults for research- Embeddings are CPU-intensive (~100ms each)
- Use
mode: "full"to skip semantic summary - Batch search requests when possible
These tools can be called in parallel:
ping+get_server_status+history- Multiple
searchqueries job_statuspolling while doing other work
These must be sequential:
research→job_status(wait for job_id)job_status→get_report(wait for report_id)
- Task: MCP 2025-11-25 protocol abstraction
- Job: Internal server job system
- Mapping: task_get/task_result wrap job_status/result
sample_messagecreates LLM completions via OpenRouter- Enables server-side agentic loops
- Uses configured models (planning_model default)
- Async jobs provide SSE URL for real-time events
- Connect to
/jobs/{job_id}/eventsfor streaming - Events: submitted, progress, completed, failed
Before using tools:
-
ping {}returns{"pong": true} -
get_server_statusshowsdatabase.initialized: true -
get_server_statusshowsembedder.ready: true -
list_tools {}returns expected tools for current mode
After research:
- Job status shows
completed - Report ID extracted from result
-
get_reportreturns content -
historyshows new report