Menu

AI

LlamaIndex MCP Tutorial: Use MCP Tools in Agents and Serve Workflows Over MCP

Load MCP server tools into a LlamaIndex FunctionAgent with BasicMCPClient and McpToolSpec, filter tools, use OAuth, and publish a Workflow as an MCP server.

By: Irina ShvayaNovember 2, 2026

LlamaIndex MCP Tutorial: Use MCP Tools in Agents and Serve Workflows Over MCP

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).
  • BasicMCPClient picks the transport from what you pass: a URL ending in /mcp uses Streamable HTTP, /sse uses SSE, and a command plus args uses stdio.
  • McpToolSpec(client=..., allowed_tools=[...]) turns server tools into LlamaIndex tools and filters them in one step.
  • BasicMCPClient can also list and read resources and prompts, not only tools.
  • workflow_as_mcp publishes a LlamaIndex Workflow as an MCP server you can test with mcp 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/mcp works, 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 show https://example.com/mcp for Streamable HTTP and https://example.com/sse for SSE.
  • McpToolSpec asks the server for its tools and wraps them. Use to_tool_list() in sync code or to_tool_list_async() inside async code.
  • FunctionAgent is 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 own httpx.AsyncClient when you need custom TLS or proxies. The README notes this works for Streamable HTTP only. Do not copy its verify=false line into production.
  • Keep secrets out of server URLs and in headers or OAuth.

Pitfalls

  • Old tutorials with /sse URLs. Switch to the server's Streamable HTTP endpoint.
  • Mixed mcp pins. LlamaIndex 0.6.0 needs MCP SDK v2; some frameworks need v1.
  • Sync calls in async code. Use to_tool_list_async() and aget_tools_from_mcp_url() inside async functions.
  • 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.

You Might Also like to Read

Get a FREE Website Audit

Enter your website and get a 20–30 page SEO, GEO, AEO & CRO audit within 24 hours. Completely free.

Don't have a site yet? Get in touch →

Ready to speak with a marketing expert?
Give us a call! (916) 866-7893

Websites Launched
1407
Websites Launched
Marketing Experts
29
Marketing Experts
Hours of Dedicated Work
204533
Hours of Dedicated Work
Irina S.Benjamin G.Jeanette M.David G.