Browse documentation
Drishti MCP for Chat
This guide covers the server-side core of using Drishti MCP inside a chat backend: connect to the hosted MCP server, expose tools to your model, run the agent loop, and stream a response. It does not cover UI components — only MCP connection, tool bridging, agent execution, and streaming.
The pattern is provider-agnostic. Drishti MCP speaks standard streamable HTTP; your agent runtime (OpenAI, Anthropic, OpenRouter, Groq, Google, or an agent framework) handles model calls and tool selection.
Mental model
Every chat integration follows the same loop, regardless of provider:
- Connect to Drishti MCP over streamable HTTP.
- List tools — Drishti returns names, descriptions, and input schemas.
- Register tools with your agent runtime in the format that runtime expects.
- Run the agent loop — the model may request tool calls; your backend executes
callToolon Drishti and returns results. - Stream or return the final answer to the client.
- Close the MCP session when the request ends.
User message
↓
Chat API route
↓
Orchestrator
↓
LLM (any provider) ──calls──► Drishti MCP tools (get_top_movers, get_news, …)
↓
Streamed response ──► clientYou do not hand-write JSON schemas for each Drishti tool. MCP listTools supplies them; your job is to bridge those definitions into your runtime and route callTool back to Drishti.
Choose an agent runtime
Pick the integration path that matches your stack. All paths use the same Drishti endpoint and Bearer auth.
| Runtime | Best when | MCP wiring |
|---|---|---|
Vercel AI SDK (ai) | You want one tool-loop API across OpenAI, Anthropic, OpenRouter, Groq, Google, and others | Bridge listTools → tool() definitions; execute with callTool |
| OpenAI Agents SDK | You want native mcpServers on an Agent and a built-in run loop | MCPServerStreamableHttp + connectMcpServers |
| Provider SDK directly | You already use OpenAI, Anthropic, or OpenRouter HTTP APIs without a framework | Map listTools to that provider's tool/function schema |
The sections below show the portable MCP client first, then two common runtime patterns. Swap the model provider without changing the Drishti connection.
Prerequisites
- A Drishti API key from the platform console.
- Node.js 18+ for the TypeScript examples.
- An API key for your LLM provider (OpenAI, Anthropic, OpenRouter, Groq, Google, etc.).
- A chat API route or backend handler where you can open one MCP session per request.
Environment variables
# Drishti MCP
DRISHTI_API_KEY=your_key_here
DRISHTI_MCP_URL=https://mcp.drishti.manasija.in
# LLM provider (use the keys your runtime needs)
OPENAI_API_KEY=sk-...
# ANTHROPIC_API_KEY=...
# OPENROUTER_API_KEY=...
# GROQ_API_KEY=...Drishti MCP uses streamable HTTP transport. Send your Drishti API key as a Bearer token on each MCP request.
Connect to Drishti MCP
Use the official MCP TypeScript SDK for a provider-neutral client. Agent frameworks may wrap this internally, but the contract is the same: connect → listTools → callTool → close.
pnpm add @modelcontextprotocol/sdkimport { Client } from "@modelcontextprotocol/sdk/client/index.js"
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
const DRISHTI_MCP_URL = process.env.DRISHTI_MCP_URL ?? "https://mcp.drishti.manasija.in"
export async function connectDrishtiMcp(apiKey: string) {
const transport = new StreamableHTTPClientTransport(
new URL(DRISHTI_MCP_URL),
{
requestInit: {
headers: { Authorization: `Bearer ${apiKey}` },
},
},
)
const client = new Client({ name: "drishti-chat", version: "1.0.0" })
await client.connect(transport)
return client
}List tools once per request (or cache per process if your traffic pattern allows):
const BLOCKED = new Set(["search_docs", "get_doc"])
export async function listMarketTools(client: Client) {
const { tools } = await client.listTools()
return tools.filter((tool) => tool.name && !BLOCKED.has(tool.name))
}For a market-research chat agent, filter out documentation tools (search_docs, get_doc) so the model stays on live data tools such as get_top_movers, get_news, and get_announcements.
Execute a tool when the model requests it:
const result = await client.callTool({
name: "get_top_movers",
arguments: { exchange: "NSE" },
})Always call client.close() when the chat request finishes.
Pattern A: Vercel AI SDK (any provider)
The Vercel AI SDK is a practical default when you want one tool-loop API and the freedom to swap models through provider packages.
Install the core packages plus one or more provider packages:
pnpm add ai zod @ai-sdk/openai @ai-sdk/anthropic @openrouter/ai-sdk-providerBridge MCP tools to AI SDK
Convert each MCP tool into an AI SDK tool() whose execute handler calls Drishti:
import { tool, streamText, stepCountIs } from "ai"
import { z } from "zod"
import type { Client } from "@modelcontextprotocol/sdk/client/index.js"
function mcpToolsToAiSdk(mcp: Client, mcpTools: { name: string; description?: string }[]) {
return Object.fromEntries(
mcpTools.map((entry) => [
entry.name,
tool({
description: entry.description ?? entry.name,
// Map MCP inputSchema to zod, or use jsonSchema() from the AI SDK
inputSchema: z.object({}).passthrough(),
execute: async (args) =>
mcp.callTool({ name: entry.name, arguments: args }),
}),
]),
)
}In production, derive each tool's inputSchema from the MCP inputSchema field instead of passthrough().
Swap the model provider
Only the model import changes. The Drishti MCP client and tool bridge stay the same.
// OpenAI
import { createOpenAI } from "@ai-sdk/openai"
const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY! })
const model = openai("gpt-4.1")
// Anthropic
import { createAnthropic } from "@ai-sdk/anthropic"
const anthropic = createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY! })
const model = anthropic("claude-sonnet-4-20250514")
// OpenRouter (route to any supported model)
import { createOpenRouter } from "@openrouter/ai-sdk-provider"
const openrouter = createOpenRouter({ apiKey: process.env.OPENROUTER_API_KEY! })
const model = openrouter("anthropic/claude-sonnet-4")Run the chat loop
export async function runDrishtiChat(
userMessage: string,
options?: { signal?: AbortSignal },
) {
const mcp = await connectDrishtiMcp(process.env.DRISHTI_API_KEY!)
try {
const mcpTools = await listMarketTools(mcp)
const tools = mcpToolsToAiSdk(mcp, mcpTools)
const result = streamText({
model, // from OpenAI, Anthropic, OpenRouter, Groq, etc.
system: [
"You are an Indian stock market research assistant (NSE/BSE).",
"Use Drishti tools for live prices, movers, news, earnings, and announcements.",
"Cite which tool produced each fact.",
].join(" "),
prompt: userMessage,
tools,
stopWhen: stepCountIs(8),
abortSignal: options?.signal,
})
return {
response: result.toUIMessageStreamResponse(),
cleanup: () => mcp.close(),
}
} catch (error) {
await mcp.close()
throw error
}
}streamText runs the tool loop: the model requests a tool, your execute handler calls Drishti, results go back to the model until it produces a final answer. Return toUIMessageStreamResponse() for AI SDK chat UIs, or toTextStreamResponse() for plain text.
Pattern B: OpenAI Agents SDK (native MCP)
If you use the OpenAI Agents SDK, you can attach Drishti as mcpServers and skip manual tool bridging. The SDK calls listTools and callTool for you.
pnpm add @openai/agents @openai/agents-extensions ai @ai-sdk/openai @openrouter/ai-sdk-providerimport {
Agent,
run,
MCPServerStreamableHttp,
connectMcpServers,
} from "@openai/agents"
import { createOpenRouter } from "@openrouter/ai-sdk-provider"
import { aisdk } from "@openai/agents-extensions/ai-sdk"
const drishti = new MCPServerStreamableHttp({
url: process.env.DRISHTI_MCP_URL ?? "https://mcp.drishti.manasija.in",
name: "Drishti",
cacheToolsList: true,
requestInit: {
headers: { Authorization: `Bearer ${process.env.DRISHTI_API_KEY}` },
},
})
const mcp = await connectMcpServers([drishti])
// Bridge any AI SDK provider — OpenAI, Anthropic, OpenRouter, Groq, etc.
const openrouter = createOpenRouter({ apiKey: process.env.OPENROUTER_API_KEY! })
const model = aisdk(openrouter("anthropic/claude-sonnet-4"))
const agent = new Agent({
name: "Market Agent",
instructions: "Answer using Drishti MCP tools for Indian market data.",
model: model as never,
mcpServers: mcp.active as never[],
})
try {
const result = await run(agent, "What are today's top movers on NSE?", {
stream: true,
})
// use createAiSdkUiMessageStreamResponse(result) to stream to the client
} finally {
await mcp.close()
}aisdk() is the bridge: your agent runtime stays the OpenAI Agents SDK, but the model can come from any Vercel AI SDK provider.
Pattern C: Provider API directly
If you call OpenAI, Anthropic, or OpenRouter HTTP APIs without a framework, the loop is the same:
listToolsfrom Drishti MCP.- Map each tool to the provider's tool or function schema.
- Send the user message plus tools to the chat completion endpoint.
- When the model returns tool calls, run
callToolon Drishti and append results to the message history. - Repeat until the model returns a final text response.
OpenRouter exposes an OpenAI-compatible chat API, so one client can route to Claude, GPT, Gemini, Llama, and other models with the same tool payload shape.
Lifecycle rules
These apply regardless of runtime:
- Open one MCP session per chat request (or per conversation if you pool sessions yourself).
- Always `close()` the MCP client in
finallyor after the stream ends. - Pass `AbortSignal` through so client disconnects cancel in-flight tool calls.
- Tell the model to use tools in system instructions — models skip tools they are not prompted to consider.
Chat API route
Pipe the stream and ensure MCP cleanup when the response completes:
import { runDrishtiChat } from "@/lib/run-chat"
export async function POST(req: Request) {
const { message } = await req.json()
if (!message?.trim()) {
return Response.json({ error: "message required" }, { status: 400 })
}
const { response, cleanup } = await runDrishtiChat(message, {
signal: req.signal,
})
const body = response.body
if (!body) {
await cleanup()
return response
}
const reader = body.getReader()
const stream = new ReadableStream({
async start(controller) {
try {
while (true) {
const { done, value } = await reader.read()
if (done) break
controller.enqueue(value)
}
} finally {
await cleanup()
controller.close()
}
},
cancel() {
void cleanup()
},
})
return new Response(stream, {
status: response.status,
headers: response.headers,
})
}The route shape is the same for Pattern A and Pattern B — only the orchestrator behind runDrishtiChat changes.
Available tools
After connect, the model can call tools such as get_top_movers, get_symbols, get_announcements, get_news, list_earnings, and search_concalls — whatever Drishti returns from listTools. See the Drishti MCP tool catalog for the full list.
Suggested project layout
| Concern | Typical location |
|---|---|
| MCP connect and teardown | mcp/drishti.ts |
| Tool list and filtering | mcp/tools.ts |
| Runtime bridge (AI SDK, Agents SDK, etc.) | agents/ or lib/agent.ts |
| Model provider config | providers/index.ts |
| Chat orchestration | lib/run-chat.ts |
| HTTP entry | app/api/chat/route.ts (or your framework equivalent) |
The chat-drishti-mcp demo on GitHub follows this layout. Try the live version or use the repo as a starting point.
Split files the way your codebase already organizes routes and shared libraries. The important boundary is one MCP connect per request and guaranteed `close()`.
Debugging
| Symptom | Likely cause |
|---|---|
| Model never calls tools | Tools not passed to the runtime, or system prompt does not mention using them |
401 from MCP | Missing or invalid DRISHTI_API_KEY |
| Tools list empty | Wrong MCP URL, server unavailable, or tools filtered out |
| Hangs after response | MCP close() not called |
| Tool schema errors | MCP inputSchema not mapped correctly to your runtime's tool format |
| Unexpected tool names in logs | Some runtimes prefix tool names with the MCP server name |
Inspect available tools after connect:
const { tools } = await client.listTools()
console.log(tools.map((t) => t.name))MCP vs SDK in production
Use Drishti MCP when the model should choose tools conversationally inside a chat or agent runtime.
Use the JavaScript / TypeScript SDK, Python SDK, or WebSocket streams when you need deterministic fetches in cron jobs, dashboards, alert pipelines, or other code paths that should not depend on LLM tool selection.
MCP tool calls consume Drishti credits on data tools. See Pricing for plan behavior.
Related docs
- Live chat demo: hosted reference app.
- chat-drishti-mcp on GitHub: source code.
- Drishti MCP: connector URL, authentication, and full tool catalog.
- Build with Drishti MCP and Coding Agents: MCP inside Cursor, Codex, Claude Code, and VS Code.
- Connect MCP to Claude: web assistant connector workflow.
- Authentication: API key setup for REST, WebSocket, and MCP.
- JavaScript / TypeScript SDK and Python SDK: production runtime clients.