
This tutorial is part of Build an MCP Server, within The MCP Guide. Want it built for you? See MCP development.
LlamaIndex supports MCP through one integration package, llama-index-tools-mcp. It gives you BasicMCPClient to connect to a server, McpToolSpec to turn the server's tools into LlamaIndex tools, and workflow_as_mcp to go the other way and publish a LlamaIndex Workflow as an MCP server.
This tutorial connects a FunctionAgent to a local server, filters the tools, adds OAuth, and exposes a small Workflow. Code was checked against the package README and the LlamaIndex MCP API reference on 2 November 2026. The current release on PyPI that day was llama-index-tools-mcp 0.6.0.
Key Takeaways
- Install
llama-index-tools-mcp; version 0.6.0 requires the official MCP Python SDK v2 (mcp>=2.0.0,<3). BasicMCPClientpicks the transport from what you pass: a URL ending in/mcpuses Streamable HTTP,/sseuses SSE, and a command plusargsuses stdio.McpToolSpec(client=..., allowed_tools=[...])turns server tools into LlamaIndex tools and filters them in one step.BasicMCPClientcan also list and read resources and prompts, not only tools.workflow_as_mcppublishes a LlamaIndex Workflow as an MCP server you can test withmcp dev.
Prerequisites
- Python 3.10 or later.
pip install llama-index-tools-mcp llama-index-llms-openai(or the LLM integration you use).- An MCP server. Any Streamable HTTP server on
http://localhost:8000/mcpworks, for example the FastMCP notes server in our langchain-mcp-adapters tutorial.
Why the MCP SDK version matters
On PyPI, llama-index-tools-mcp 0.6.0 requires mcp>=2.0.0,<3. Some other agent frameworks still pin mcp<2. If you mix LlamaIndex with one of those in the same virtual environment, the resolver will fail. Keep frameworks with conflicting pins in separate environments.
The SDK v2 change also affects servers you write. The Python SDK v2 removed the old mcp.server.fastmcp import path; use from mcp.server import MCPServer, or the standalone fastmcp package. Our FastMCP tutorial covers the second option.
Minimal working example
import asyncio
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.llms.openai import OpenAI
from llama_index.tools.mcp import BasicMCPClient, McpToolSpec
async def main():
mcp_client = BasicMCPClient("http://localhost:8000/mcp")
mcp_tool_spec = McpToolSpec(client=mcp_client)
tools = await mcp_tool_spec.to_tool_list_async()
agent = FunctionAgent(
name="Agent",
description="Answers questions using the notes server.",
llm=OpenAI(model="gpt-4o"),
tools=tools,
system_prompt="You are a helpful assistant. Use tools to look things up.",
)
resp = await agent.run("What does the welcome note say?")
print(resp)
asyncio.run(main())
What each piece does:
BasicMCPClient(url)connects. The README's examples showhttps://example.com/mcpfor Streamable HTTP andhttps://example.com/ssefor SSE.McpToolSpecasks the server for its tools and wraps them. Useto_tool_list()in sync code orto_tool_list_async()inside async code.FunctionAgentis LlamaIndex's tool-calling agent. Swap in any LLM integration that supports function calling.
The README's quick example still points at an /sse URL. SSE is the deprecated MCP transport, so use the server's /mcp endpoint when it has one. Our SSE vs Streamable HTTP page explains the difference.
Local servers over stdio
Pass a command and arguments instead of a URL:
local_client = BasicMCPClient("python", args=["server.py"])
LlamaIndex starts the script as a subprocess and talks to it over stdin and stdout. Use this for local tools and tests, not for shared services.
Filter tools
Give the agent only what it needs:
mcp_tool_spec = McpToolSpec(
client=mcp_client,
allowed_tools=["list_notes", "read_note"],
include_resources=False,
)
allowed_tools filters by name. include_resources=True adds the server's resources to the tool list, so the agent can read them as if they were tools. Leave it off unless the agent needs them, since each extra entry adds text to every model call. Our too many MCP tools guide explains the cost.
There is also a one-line helper if you do not need filtering:
from llama_index.tools.mcp import aget_tools_from_mcp_url
tools = await aget_tools_from_mcp_url("http://localhost:8000/mcp")
Note: the README's async example calls get_tools_from_mcp_url with await. Use aget_tools_from_mcp_url in async code; the a prefix is the async version.
Use the client directly
BasicMCPClient is more than a tool loader. The README shows:
tools = await http_client.list_tools()
result = await http_client.call_tool("calculate", {"x": 5, "y": 10})
resources = await http_client.list_resources()
content, mime_type = await http_client.read_resource("config://app")
prompts = await http_client.list_prompts()
prompt_result = await http_client.get_prompt("greet", {"name": "World"})
This is handy for debugging, and for apps that feed a resource into a query engine rather than letting the agent decide.
OAuth
For servers protected by OAuth, the README shows BasicMCPClient.with_oauth(...) with a client_name, redirect_uris, a redirect_handler that opens or prints the sign-in URL, and a callback_handler that returns the authorization code. Tokens are kept in memory by default. For production, pass a token_storage that implements the MCP SDK's TokenStorage interface so tokens survive restarts.
Store tokens like passwords. The README's file-storage sample writes them to tokens.json in plain text, which is fine for a demo and wrong for a shared server.
Add a tool
Tools live on the server. Add a function there, restart it, and call to_tool_list_async() again. If you set allowed_tools, add the new name to the list, or the agent will not see it.
Expose a Workflow as an MCP server
workflow_as_mcp turns a LlamaIndex Workflow into an MCP server. The README's example:
from llama_index.core.workflow import (
Context,
Workflow,
Event,
StartEvent,
StopEvent,
step,
)
from llama_index.tools.mcp import workflow_as_mcp
class RunEvent(StartEvent):
msg: str
class InfoEvent(Event):
msg: str
class LoudWorkflow(Workflow):
"""Useful for converting strings to uppercase and making them louder."""
@step
def step_one(self, ctx: Context, ev: RunEvent) -> StopEvent:
ctx.write_event_to_stream(InfoEvent(msg="Hello, world!"))
return StopEvent(result=ev.msg.upper() + "!")
workflow = LoudWorkflow()
mcp = workflow_as_mcp(workflow, start_event_model=RunEvent)
The start_event_model defines the tool's input. The workflow's docstring becomes the description, so write it for the calling model. The README launches it with mcp dev script.py (needs the mcp[cli] extra), which opens the MCP Inspector against your server.
Test with the MCP Inspector
For any server, local or remote, the Inspector CLI lists tools without involving an LLM:
npx @modelcontextprotocol/inspector --cli http://localhost:8000/mcp --transport http --method tools/list
If the Inspector sees a tool and your agent does not, check allowed_tools first. Our MCP Inspector lesson covers tool calls and CI checks.
MCP alongside your RAG pipeline
Many LlamaIndex apps already have a retrieval pipeline over company documents. MCP does not replace it. A sensible split:
- Keep retrieval in LlamaIndex for search over your own indexed documents, where you control chunking, embeddings and ranking.
- Use MCP tools for live systems such as a CRM, a ticket queue or a database, where the answer changes by the minute and the server owner already maintains the integration.
- Use MCP resources for fixed context such as a config file or a policy document a server publishes. Read them with
read_resource()and pass them in yourself, rather than letting the agent decide.
You can also publish your RAG pipeline to other clients by wrapping it in a Workflow and calling workflow_as_mcp, as shown above. Then Claude or Cursor can query the same index your LlamaIndex agent uses.
Deploy notes
- Run shared servers over Streamable HTTP, not stdio.
- Use
BasicMCPClient(url=..., http_client=...)with your ownhttpx.AsyncClientwhen you need custom TLS or proxies. The README notes this works for Streamable HTTP only. Do not copy itsverify=falseline into production. - Keep secrets out of server URLs and in headers or OAuth.
Pitfalls
- Old tutorials with
/sseURLs. Switch to the server's Streamable HTTP endpoint. - Mixed
mcppins. LlamaIndex 0.6.0 needs MCP SDK v2; some frameworks need v1. - Sync calls in async code. Use
to_tool_list_async()andaget_tools_from_mcp_url()insideasyncfunctions. - Trusting every server. Tool descriptions go straight into the model's context. Read our tool poisoning guide before connecting third-party servers.
Next step
Take the free MCP course to understand the protocol under these helpers. If you want a reliable MCP server for your LlamaIndex agents or RAG stack, our MCP development team can build it with you.
More in the MCP Guide
Free tools: server.json Validator · MCP Error Code Lookup
Latest research: State of MCP: October 2026
Frequently asked questions
What package do I install for MCP in LlamaIndex?
Install llama-index-tools-mcp. It provides BasicMCPClient, McpToolSpec, the get_tools_from_mcp_url helpers and workflow_as_mcp. Add an LLM integration such as llama-index-llms-openai for the agent.
Does LlamaIndex support Streamable HTTP MCP servers?
Yes. The README shows BasicMCPClient("https://example.com/mcp") as the Streamable HTTP form. URLs ending in /sse use the older SSE transport.
Can a LlamaIndex agent read MCP resources?
Yes. Either set include_resources=True on McpToolSpec so resources appear as tools, or call list_resources() and read_resource() on BasicMCPClient yourself.
How do I expose my LlamaIndex pipeline to Claude or Cursor?
Wrap it in a Workflow and pass it to workflow_as_mcp with a start_event_model. Then run the resulting server and add it to your client like any other MCP server.











