Skip to content

Fix MCP buyer flow: search, agent registration, Linq wiring - #2

Merged
Priyank911 merged 1 commit into
mainfrom
fix/mcp-e2e-rental-flow
Aug 1, 2026
Merged

Fix MCP buyer flow: search, agent registration, Linq wiring#2
Priyank911 merged 1 commit into
mainfrom
fix/mcp-e2e-rental-flow

Conversation

@Anarv2104

Copy link
Copy Markdown
Collaborator

Summary

Tested the actual MCP buyer path end-to-end (search → rent → execute_rental_task through real MCP tool calls, not direct API calls) and fixed everything that was a code bug. Two things remain blocked on external input — see below, please read before re-debugging them.

What's fixed

  • src/mcp/server.js: db.* calls used prepared statements as plain async functions instead of .get()/.run()/.all() — broke register_agent, get_listing_detail, my_purchases, usage logging, etc.
  • src/mcp/server.js: search tool sent a GET with query params; the backend route is POST-only expecting a JSON body.
  • src/mcp/server.js: search response unwrap read data.data instead of data.data.results, crashing with results.map is not a function.
  • src/routes/marketplace.js: FTS5 MATCH got the raw natural-language query. FTS5 barewords default to AND, so a sentence like "I need to rent a remote agent for code review" required every word to appear in the listing text and returned zero rows — before semantic re-ranking ever got a chance to run. Now strips stopwords and OR-joins the remaining terms.
  • src/services/linq.js: called linqClient.messages.send(), which doesn't exist on the installed @linqapp/sdk. Real API is linqClient.chats.create({from, to, message}).
  • .mcp.json had a machine-specific absolute Node path baked in — gitignored it and added .mcp.json.example instead, plus a README note about a real, confirmed gotcha: better-sqlite3 is a native module compiled against a specific Node version; running the server with a different node on PATH causes a silent NODE_MODULE_VERSION mismatch crash. Cost real debugging time this session — worth reading before assuming a fresh clone "just works."

Verified working

  • MCP search → real semantic re-ranking via Groq (confirmed by absence of the fallback/error log, not just by there being one listing).
  • rent → Prava mandate flow reaches the real API (see blocker below on the key itself).
  • Payment gate on execute_rental_task: confirmed it correctly blocks unapproved transactions (402) and allows approved ones — tested both paths, including manually flipping a transaction to approved in SQLite to isolate the A2A handoff.
  • A2A handoff: execute_rental_task reaches the live-agent process over localhost:9001 and returns a real LLM-backed code review (issues, severities, recommendations) through the MCP tool result.

Blocked — not code bugs, need external input

  • Prava: createMandate gets a real 401 AUTH_1001 Invalid API key from two different valid-looking sandbox keys. Falls back to a mock approval_url. Needs a response from Prava/hackathon support on whether the team's sandbox is actually activated — not a config typo, already checked the key format and API URL.
  • Linq: send now reaches the real API (the SDK-method bug above is fixed) but gets rejected with 'to' must not include the 'from' address.env only defines one phone number (LINQ_PHONE_NUMBER), used as both sender and recipient. Needs a USER_PHONE_NUMBER (or similar) env var with an actual distinct recipient number to test a real send.
  • Capture step: chargeMandate/reportMandateCharge are implemented in src/services/prava.js but never called anywhere in the rent/execute flow — capture is currently just a local status flip to captured on A2A task completion. This is a team decision, not a bug: decide whether the demo needs an actual captured charge, or whether mandate-created + approved + task-executed is sufficient.

Test plan

  • register_agent via MCP succeeds
  • search via MCP returns the live listing for a natural-language query
  • Backend log shows no LLM-fallback warning during search (real Groq ranking ran)
  • rent via MCP returns a mandate response (mock, due to the Prava 401 above)
  • execute_rental_task blocked with 402 before approval
  • execute_rental_task succeeds with a real A2A response after manually approving the transaction in SQLite
  • Real Prava mandate approval flow (blocked on valid sandbox key)
  • Real Linq message delivery (blocked on distinct recipient number)

🤖 Generated with Claude Code

Fixes surfaced during a real end-to-end test through the MCP buyer
path (search -> rent -> execute_rental_task), not direct API calls:

- src/mcp/server.js: db.* calls used prepared statements as plain
  async functions instead of .get()/.run()/.all() (register_agent,
  get_listing_detail, my_purchases, etc. were all broken)
- src/mcp/server.js: search tool sent GET with query params, but the
  backend route is POST-only expecting a JSON body
- src/mcp/server.js: search response unwrap read data.data instead of
  data.data.results, so formatting crashed with "results.map is not
  a function"
- src/routes/marketplace.js: FTS5 MATCH received the raw natural-
  language query, and FTS5 barewords default to AND, so any sentence
  with words not in the listing text (e.g. "I need to rent...")
  returned zero rows before semantic re-ranking ever ran. Now strips
  stopwords and OR-joins remaining terms.
- src/services/linq.js: called linqClient.messages.send(), which
  doesn't exist on the installed @linqapp/sdk; real API is
  linqClient.chats.create({from, to, message}).

Not fixed here (need input from you/the team, see PR description):
- Prava mandate creation 401s (AUTH_1001 Invalid API key) on the
  configured sandbox key -- falls back to a mock approval_url.
- Linq send now reaches the real API but is rejected with
  "'to' must not include the 'from' address" -- .env only has one
  phone number (LINQ_PHONE_NUMBER), used as both sender and
  recipient. Needs a distinct recipient number.
- chargeMandate/reportMandateCharge are implemented in
  src/services/prava.js but never called from the rent/execute
  route -- capture is currently just a local status flip.

Also: gitignore .mcp.json (had a machine-specific absolute node
path) and add .mcp.json.example instead; document the better-
sqlite3 native-module / Node-version gotcha in README since it
caused a silent MCP crash during this debugging session.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 1, 2026 19:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes and hardens the end-to-end MCP buyer flow (agent registration → search → rent → execute task) by correcting DB statement usage, aligning MCP tool calls with REST semantics, and adding a real LLM-backed live-agent A2A server for rentals.

Changes:

  • Fix MCP server DB usage + search request/response wiring; add get_rental_status tool.
  • Improve marketplace search behavior for natural-language queries (FTS stopword stripping + optional semantic re-ranking).
  • Introduce shared llm-client and a new live-agent service to provide real A2A code review responses.

Reviewed changes

Copilot reviewed 18 out of 21 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/services/prava.js Adds mandate-status polling and extra diagnostics for Prava flows.
src/services/openai.js Switches OpenAI usage to shared LLM client abstraction.
src/services/llm-client.js New shared LLM client provider selector (Groq/OpenAI).
src/services/linq.js Updates Linq send API wiring to the SDK’s chats.create.
src/services/a2a-client.js Extends parsed A2A task responses with output and clarification fields.
src/server.js Simplifies dotenv initialization.
src/routes/marketplace.js Fixes natural-language FTS behavior, adds semantic ranking, adds rental status endpoint, and tightens execute gating.
src/mcp/server.js Fixes DB prepared statement usage, switches search to POST JSON, fixes response unwrapping, adds get_rental_status.
src/db/seed.js Adds a seed path for a default live-agent listing pointing to localhost A2A server.
README.md Updates MCP tool count and documents better-sqlite3 Node version mismatch gotcha + .mcp.json workflow.
package.json Adds live-agent script entry.
package-lock.json Updates locked zod version.
live-agent/server.js New real A2A live agent implementation backed by configured LLM provider.
live-agent/README.md Documents running and behavior of the new live agent.
live-agent/package.json Adds standalone package metadata for the live agent.
live-agent/package-lock.json Lockfile for live-agent dependencies.
docs/implementation_plan.md Adds a detailed implementation plan document.
docs/gemini-code-1785578177707.md Adds documentation/guide content about sellable agentic assets.
.mcp.json.example Adds example MCP client config file.
.gitignore Ignores machine-specific .mcp.json.
.env.example Adds LLM provider config and live-agent port; tweaks sandbox expiry example.
Files not reviewed (1)
  • live-agent/package-lock.json: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/services/prava.js
Comment on lines +3 to 7
if (!process.env.PRAVA_API_URL) {
console.warn('[PravaService] PRAVA_API_URL not set in .env — falling back to default, this will likely fail against real sandbox');
}

const PRAVA_API_URL = process.env.PRAVA_API_URL || 'https://api.prava.com';
Comment thread src/services/prava.js
Comment on lines +145 to +147
const data = await response.json();
console.log('[PravaService] Raw createMandate response:', JSON.stringify(data, null, 2));
return data;
Comment thread src/routes/marketplace.js
Comment on lines 48 to 52
if (query) {
try {
results = searchListingsFTS.all(query, limit, offset);
const ftsQuery = buildFtsQuery(query);
results = ftsQuery ? searchListingsFTS.all(ftsQuery, limit, offset) : [];
} catch (e) {
Comment thread src/routes/marketplace.js
Comment on lines +358 to +360
const tx = getTransactionById.get(req.params.txId);
if (!tx) throw new AppError('NOT_FOUND', 404, 'Transaction not found');

Comment thread package.json
Comment on lines +11 to +12
"demo-agent": "node demo-agent/server.js",
"live-agent": "node live-agent/server.js"
Comment thread src/services/linq.js
Comment on lines +13 to +17
await linqClient.chats.create({
from: process.env.LINQ_PHONE_NUMBER,
to: [phoneNumber],
message: { parts: [{ type: 'text', value: text }] }
});
Comment thread src/mcp/server.js
}, toolHandler(async ({ transaction_id, agent_id }) => {
await validateAgentAndLogUsage(agent_id, 'get_rental_status', { transaction_id });

const data = await fetchApi(`/rent/${transaction_id}/status`);
@Priyank911
Priyank911 merged commit 5d9619c into main Aug 1, 2026
2 checks passed
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