Expose LlamaIndex Tool Specs as an MCP Server with fastmcp

September 20, 2026guides
MCP

This guide is adapted from LlamaIndex's mcp_agent_tools.ipynb, published under the MIT licence.


The Model Context Protocol (MCP) is emerging as the USB-C of AI tool integration: a single, standardised interface that lets any compliant client discover and call any compliant server without bespoke glue code. LlamaIndex ships dozens of battle-tested tool specs on LlamaHub, each wrapping a real external service — Notion, Slack, Wikipedia, and more. What this guide shows is how to take any of those existing tool specs and expose them as a fully compliant MCP server in roughly fifty lines of code, making them immediately consumable by Claude Desktop, OpenAI's agent runtime, or any other MCP-aware client. If you are building production agentic systems and want the flexibility described in coverage like Amazon Bedrock AgentCore's MCP app support, this pattern is the lightweight self-hosted equivalent.

The technique matters because it separates tool authorship from agent authorship. A platform team can publish an MCP server wrapping internal APIs once; every downstream agent — regardless of the framework it uses — calls those tools without re-implementing authentication, rate-limit handling, or schema definition. LlamaIndex's tool specs already handle all of that logic; fastmcp turns the resulting Python callables into HTTP endpoints that expose standardised JSON schemas automatically.

This approach is genuinely lightweight. A single CPU core and 512 MB of RAM are sufficient to host the server during development. You will need a Notion integration token (free) and an OpenAI-compatible model endpoint only if you want to wire a full agent on top. There are no GPU requirements at any point in this workflow.

Prerequisites

  • Python 3.10 or later. fastmcp uses asyncio features that rely on modern syntax.
  • A Notion integration token. Create one at notion.so/my-integrations and share at least one page with the integration.
  • An async-capable Python environment. The final server step uses await, so run it inside a Jupyter notebook or wrap it in asyncio.run() in a standalone script.
  • Network egress to api.notion.com and, if you add an LLM agent on top, to your model provider endpoint.

Step 1: Install dependencies

The entire dependency surface is three packages. llama-index-tools-notion pulls in the Notion tool spec and the core LlamaIndex abstractions it inherits from. mcp is the reference Python SDK for the Model Context Protocol. fastmcp is the high-level server framework built on top of it — it handles schema generation, transport negotiation, and the async event loop.

!pip install llama-index-tools-notion mcp fastmcp

Pinning versions in production is worth the extra line in your requirements.txt. fastmcp is a fast-moving project and breaking changes between minor versions have appeared in the past. Lock to a known-good combination before deploying.

Step 2: Import MCP server dependencies

With the packages installed, import the server primitives and verify the import succeeds cleanly. This step is deliberately separate from the tool setup so any environment issues surface early, before you spend time configuring credentials.

# Import dependencies for Model Context Protocol (MCP) fastMCP server
from typing import Any, Dict, List, Optional
from fastmcp import FastMCP

print("MCP fastMCP server dependencies imported successfully!")

The typing imports are not used directly in this cell but are needed by some of the tool spec internals that get pulled in at registration time. Omitting them can produce runtime errors when the server tries to introspect function signatures during schema generation.

Step 3: Instantiate the LlamaIndex tool spec

LlamaIndex tool specs are thin wrappers around service SDKs that conform to a common interface: each spec exposes a to_tool_list() method that returns a list of BaseTool objects. Each tool carries structured metadata (name, description, argument schema) and a callable real_fn. This standardised shape is what lets the bridge to MCP work generically across any spec on LlamaHub.

# Import and configure LlamaIndex Notion Tool Spec
from llama_index.tools.notion import NotionToolSpec

notion_token = "xxxx"
tool_spec = NotionToolSpec(integration_token=notion_token)

Replace "xxxx" with your actual integration token. In production, pull this from an environment variable rather than hard-coding it. The NotionToolSpec constructor validates the token format but does not make a live API call at instantiation time, so an invalid token will only surface when a tool is actually invoked.

Now materialise the tool list and inspect what you have:

tools = tool_spec.to_tool_list()

for i, tool in enumerate(tools):
    print(f"Tool {i+1}: {tool.metadata.name}")

The Notion spec surfaces tools for searching pages, reading page content, and appending block content, among others. Printing them here is a useful sanity check that your token has the right scopes — if a tool that should exist is missing, the integration probably lacks the corresponding Notion capability grant.

Step 4: Create the MCP server and register tools

A FastMCP instance is created with a human-readable server name, then each LlamaIndex tool is registered onto it by passing the tool's metadata directly and pointing at its underlying Python function.

mcp_server = FastMCP("MCP Agent Tools Server")

# Register the tools from the Notion ToolSpec
for tool in tools:
    mcp_server.tool(
        name=tool.metadata.name, description=tool.metadata.description
    )(tool.real_fn)

The mcp_server.tool(...) decorator call is where fastmcp introspects tool.real_fn's type annotations to generate the JSON Schema that MCP clients use for tool discovery. The quality of the schema the client sees is therefore directly determined by how well the underlying LlamaIndex tool function is annotated. Well-maintained LlamaHub specs tend to have full annotations; community-contributed ones may not, leading to sparse schemas. You can augment descriptions at registration time since you control the description= argument.

Step 5: Run the MCP server

With all tools registered, start the server. The streamable-http transport exposes a standard HTTP endpoint that MCP clients can connect to and stream results from.

await mcp_server.run_async(transport="streamable-http")

By default, fastmcp binds to localhost:8000. In a Jupyter environment this cell blocks — the server runs in the foreground until you interrupt the kernel. In a production deployment, wrap the server startup in a proper ASGI application so it can be managed by a process supervisor.

Transport and deployment options

Transport Use case Persistent connection Suitable for production
streamable-http Remote clients, multi-user agents Yes (SSE stream) Yes, behind a reverse proxy
stdio Local desktop clients (e.g. Claude Desktop) No (process per session) Development only
sse Legacy MCP clients expecting pure SSE Yes Conditional on client support

What to watch out for

Async context requirement. run_async must be awaited inside a running event loop. Calling it from a synchronous script without wrapping it raises a RuntimeError. Use asyncio.run(mcp_server.run_async(transport="streamable-http")) outside notebooks.

Token leakage in schema descriptions. fastmcp exposes tool descriptions verbatim to every client that connects. If a LlamaHub tool spec embeds credential hints or internal endpoint URLs in its description string, those become visible to any connecting client — including untrusted ones. Audit descriptions before deploying publicly.

Rate limits are your responsibility. LlamaIndex tool specs handle authentication but not throttling. Once your MCP server is reachable by multiple agent instances simultaneously, you can trivially exceed Notion's API rate limits. Add a semaphore or token-bucket middleware around real_fn for anything beyond a single-agent demo.

Schema completeness varies by spec. fastmcp generates schemas from type annotations. Tools with **kwargs or untyped arguments produce schemas that some strict MCP clients reject outright. Test with your target client before committing to a spec.

No authentication on the server itself. The pattern as shown starts an unauthenticated HTTP server. Anyone who can reach the port can call your Notion integration. In deployment, put the server behind a gateway that handles mTLS or bearer-token verification before requests reach fastmcp.

Stateless vs. stateful tools. MCP assumes tools are stateless and idempotent by convention. Several LlamaIndex specs maintain internal state (caches, session handles). Running multiple concurrent requests through real_fn without understanding that state can produce race conditions.

Where to go next

The same registration loop in Step 4 works identically for every other tool spec on LlamaHub — swap NotionToolSpec for SlackToolSpec, WikipediaToolSpec, or any other, and the MCP server exposes those tools without further changes. For multi-agent deployments where different agents specialise in different tool domains, the pattern described in DoorDash's multi-agent feature flag cleanup shows how to compose specialised servers effectively at the orchestration layer. The OpenAI Agents API public beta and ToolGrad's function-calling benchmark analysis are worth reading alongside this guide to understand how tool-calling quality is evaluated at scale. For the complete runnable notebook this guide is derived from, see the LlamaIndex repository directly.

Frequently asked questions

What packages do I need to install to expose a LlamaIndex tool spec as an MCP server?

You need exactly three packages: `llama-index-tools-notion` (or the equivalent spec for your chosen service), `mcp` (the reference Python SDK for the Model Context Protocol), and `fastmcp` (the high-level server framework). Install them with `pip install llama-index-tools-notion mcp fastmcp`. Pin versions in production because `fastmcp` has introduced breaking changes between minor releases.

Can I use this pattern with LlamaHub tool specs other than Notion?

Yes. The registration loop in Step 4 is generic: every LlamaHub tool spec implements `to_tool_list()`, which returns `BaseTool` objects with `metadata.name`, `metadata.description`, and `real_fn`. Swap `NotionToolSpec` for `SlackToolSpec`, `WikipediaToolSpec`, or any other compliant spec and the MCP server exposes those tools without further changes to the registration code.

How do I run the fastmcp server outside a Jupyter notebook?

Replace `await mcp_server.run_async(transport="streamable-http")` with `asyncio.run(mcp_server.run_async(transport="streamable-http"))` in a standalone Python script. Calling `run_async` without an active event loop raises a `RuntimeError`. In production, wrap the server in a proper ASGI application so a process supervisor can manage it.

How do I connect Claude Desktop or another MCP client to this server?

For a remote client use the `streamable-http` transport, which binds by default to `localhost:8000` and exposes a standard HTTP endpoint with SSE streaming. For Claude Desktop specifically, the `stdio` transport is the conventional choice: it spawns a process per session with no persistent HTTP binding. Set the transport string in the `run_async` call to match what your client expects.

Does this MCP server require a GPU?

No. The MCP server itself is pure Python network I/O; it proxies calls to external service APIs such as Notion. A single CPU core and 512 MB of RAM are sufficient for development hosting. GPU resources only become relevant if you attach a locally-hosted LLM agent on top, which this guide does not require.

What happens if a LlamaIndex tool function has untyped arguments?

fastmcp generates JSON Schemas by introspecting Python type annotations on `real_fn`. Functions that use `**kwargs` or leave arguments untyped produce incomplete schemas. Some strict MCP clients will reject those schemas outright. Audit annotations on any community-contributed LlamaHub spec before registering it, and test with your specific client before committing.

Free interactive tools for the decisions this piece raises.

Related Guides