WEIR

LlamaIndex MCP: Connect a Remote Server with McpToolSpec

Load LlamaIndex MCP tools from a remote server: BasicMCPClient with a bearer header, allowed_tools, a hard search limit, the real 401 and real X output.


Published · Updated · 6 min read

Cover image: MCP tools in a LlamaIndex agent

LlamaIndex MCP support comes from the llama-index-tools-mcp package: BasicMCPClient connects to a server, and McpToolSpec turns its tools into LlamaIndex tools.

This guide is for Python developers building a LlamaIndex agent. It connects Weir's remote MCP server for public X (Twitter) data with an API key, caps what each search can spend and shows a real call. We run Weir.

import asyncio, os
from llama_index.tools.mcp import BasicMCPClient, McpToolSpec

client = BasicMCPClient(
    "https://api.weirapp.io/mcp",
    headers={"Authorization": f"Bearer {os.environ['WEIR_API_KEY']}"},
)
spec = McpToolSpec(client=client, allowed_tools=["search_x", "get_x_post"])
tools = asyncio.run(spec.to_tool_list_async())
print([t.metadata.name for t in tools])

How to connect a LlamaIndex MCP client to a remote server

You need the MCP package, an LLM package, a Weir key and a model key.

  1. Installpip install llama-index-tools-mcp llama-index-llms-openai
  2. Get a Weir keyCreate one under API keys in your Weir dashboard. Keep it in WEIR_API_KEY.
  3. Set a model keyThe OpenAI LLM class reads OPENAI_API_KEY.
  4. Run the agentLoad the tools with to_tool_list_async and pass them to a FunctionAgent.

As of 24 October 2026, PyPI lists llama-index-tools-mcp 0.6.0 for Python 3.10 or later. It needs the mcp SDK 2.x and llama-index-core below 0.15.

The client picks the transport from the URL. A URL ending in /sse gets SSE, and any other https URL gets Streamable HTTP, which is what Weir serves.

LlamaIndex MCP example: a FunctionAgent that searches X

The script is 24 lines, in two parts. Part one builds the client and the tool spec:

import asyncio, os
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.llms.openai import OpenAI
from llama_index.tools.mcp import BasicMCPClient, McpToolSpec

client = BasicMCPClient(
    "https://api.weirapp.io/mcp",
    headers={"Authorization": f"Bearer {os.environ['WEIR_API_KEY']}"},
)
spec = McpToolSpec(
    client=client,
    allowed_tools=["search_x", "get_x_post"],
    partial_params_by_tool={"search_x": {"limit": 5}},
)

Part two loads the tools, builds the agent and asks:

async def main(question: str) -> None:
    tools = await spec.to_tool_list_async()
    agent = FunctionAgent(
        tools=tools,
        llm=OpenAI(model="gpt-5-mini"),
        system_prompt="Search public X posts. Link every post you cite.",
    )
    print(await agent.run(question))

asyncio.run(main("What have @llama_index and @googledevs posted about MCP?"))

Any LlamaIndex LLM with function calling can stand in for OpenAI. The MCP part stays the same.

Here's what the 0.6.0 source does with it:

  • limit is fixed at 5. The model never sees the field, so it can't ask for 50 posts.
  • Each call opens a new MCP session. That suits Weir, which keeps no state between requests.
  • A failed call doesn't end the run. FunctionAgent turns the error into a tool message the model reads.

What does the agent get back?

To answer, the model calls search_x with both handles. We made that call through Weir's MCP server on 24 October 2026:

→ search_x {"fromUsers": ["llama_index", "googledevs"], "query": "MCP -filter:nativeretweets",
            "onlyOriginal": true, "limit": 3}
← 3 posts · 4 credits · nextCursor set
  2026-10-16  890 likes  @googledevs   "Reconstructing ancient ruins in 3D with Gemini 4 Argon"
  2026-10-07  560 likes  @googledevs   Antigravity with the Stitch MCP and the Android CLI
  2026-07-30   65 likes  @llama_index  "Parse Gateway", "also available as an MCP server"

Each post carries its url, text and counts, so the agent can link what it cites. The result also has meta.next, the follow-up call with the new cursor.

4credits for the search above
11credits for a 10-post search
1credit for a search that finds nothing
5,000free Weir credits every month

Weir bills 1 credit per call plus 1 per X post, and failed calls are free. Packs start at $5 for 25,000 credits, as of October 2026. Every rate is on our pricing page.

Which LlamaIndex MCP tools does the agent see?

Without a filter, the agent gets every tool your key allows.

That's more than 40 on a default Weir key. Each tool adds its description to the prompt, and a long list means more wrong picks. McpToolSpec has four settings that shape the list:

Setting What it does
allowed_tools Keeps only the tool names you list
partial_params_by_tool Fixes one tool's arguments and hides them
global_partial_params Fixes arguments for every tool
include_resources Adds the server's resources as extra tools

Use the per-tool form for limit. A global value goes to every tool, even ones with no such field.

Names arrive as Weir sends them, and 0.6.0 has no prefix option. You can also shorten the list on Weir's side: a key created without Reddit doesn't list Weir's 12 Reddit tools.

Why does the model read every result twice?

McpToolSpec hands the model str() of the whole MCP result. Weir sends its JSON as text and again as structuredContent, so both copies reach the prompt.

This subclass passes on the text blocks only. It overrides a private method, so pin the package version:

class TextOnlySpec(McpToolSpec):
    def _create_tool_fn(self, tool_name: str):
        async def call(**kwargs):
            result = await self.client.call_tool(tool_name, kwargs)
            return "\n".join(b.text for b in result.content if b.type == "text")
        return call

Use TextOnlySpec where the script uses McpToolSpec. We rebuilt the tool message from our real 3-post result both ways. It fell from about 11,700 characters to about 5,900.

If it doesn't work

This is what to_tool_list_async() raised when we connected with no key:

ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
  ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
    mcp.shared.exceptions.MCPError: Server returned an error response

The status is hidden. Add logging.basicConfig(level=logging.INFO) and httpx2 prints "HTTP/1.1 401 Unauthorized". Then check the key with Weir's free GET /v1/whoami. Other errors:

  • "In an asynchronous environment, synchronous calls are not supported". You called to_tool_list() in a notebook or async app. Await to_tool_list_async().
  • get_tools_from_mcp_url fails on Weir. That helper builds a client with no headers. Pass client=BasicMCPClient(url, headers=...).
  • A search fails after 30 seconds. That's the default timeout on BasicMCPClient. Pass timeout=60.
  • A tool returns 402. Your Weir credits ran out, and retrying won't help.

When not to use Weir

  • Your agent has to post or DM on X. Weir is read-only.
  • You want private data. Weir reads public posts, not bookmarks, DMs or home timelines.
  • Price per post matters most. TwitterAPI.io lists $0.15 per 1,000 tweets as of October 2026, less than our $5 pack.
  • You need a guarantee. Weir isn't licensed by X or affiliated with it, so access can change.

FAQ

Does LlamaIndex support MCP?

Yes. The llama-index-tools-mcp package connects to MCP servers over Streamable HTTP, SSE or stdio. McpToolSpec turns their tools into FunctionTool objects for any LlamaIndex agent, such as FunctionAgent.

How do I pass headers to an MCP server in LlamaIndex?

Pass headers={...} to BasicMCPClient. It builds one HTTP client with them and reuses it for every session. For sign-in servers, BasicMCPClient.with_oauth() runs the OAuth flow instead.

Can LlamaIndex be an MCP server?

Yes. workflow_as_mcp in the same package wraps a LlamaIndex Workflow as an MCP server. It returns the mcp SDK's MCPServer, with the workflow as one tool.

Does LlamaIndex.TS support MCP?

It had an mcp() helper in @llamaindex/tools. As of 24 October 2026, the LlamaIndex.TS repo is archived and npm marks that package deprecated. In TypeScript, see our Vercel AI SDK guide.

Sources

All checked on 24 October 2026.