> ## Documentation Index
> Fetch the complete documentation index at: https://docs.unstructured.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Model Context Protocol (MCP) Hands-On Walkthrough for the Unstructured API's workflow operations

[Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) is an open protocol that standardizes
how applications provide context to LLMs. From the MCP documentation:

> Think of MCP like a USB-C port for AI applications. Just as USB-C provides a standardized way to connect your devices to various peripherals and accessories, MCP provides a standardized way to connect AI models to different data sources and tools.

This article provides a hands-on, step-by-step walkthrough to build a simple example on your local development machine
that integrates the [Unstructured API](/api-reference/overview) with the
[Python SDK for Model Context Protocol](https://github.com/modelcontextprotocol/python-sdk). Specifically,
this walkthrough demonstrates how to write and run Python code to create an MCP *server*
(a lightweight program that exposes specific capabilities through MCP); this MCP server uses the
[Unstructured API's workflow operations](/api-reference/workflow/overview) to list all of your available
[source connectors](/api-reference/workflow/sources/overview) and to get information about a specific source connector. The
Unstructured API's workflow operations offer a full range of partitioning, chunking, embedding, and enrichment options for your files and data.

<Note>
  For a hands-on walkthrough of the legacy [Unstructured Partition Endpoint](/api-reference/legacy-api/partition/overview) instead,
  see the [MCP Hands-On Walkthrough for the legacy Unstructured Partition Endpoint](/examplecode/tools/mcp-partition), which
  is intended for rapid prototyping of Unstructured's various partitioning strategies, with limited support for chunking,
  processing local files one file at a time.
</Note>

You call this MCP server from an MCP *client* (an application that maintains a one-to-one connection with an
MCP server), which in this walkthrough is [Claude for Desktop](https://claude.ai/download), a chat-based interface, by asking it plain language questions.
Other MCP clients are also available. [Learn more](https://modelcontextprotocol.io/clients).

At the end of this walkthrough, you can extend this example by using the Unstructured API's workflow operations to perform other tasks, such as creating, listing, changing, and
deleting various connectors, workflows, and jobs in your Unstructured account.

## Requirements

To complete this walkthrough, you must first have an Unstructured account, an Unstructured API key for that account,
the necessary Python toolchain and project setups, and Claude for Desktop, as follows.

### Unstructured account and API key

Before you begin, you must have an Unstructured account and an Unstructured API key, as follows:

1. If you do not already have an Unstructured account, [sign up for free](https://unstructured.io/?modal=try-for-free).
   After you sign up, you are automatically signed in to your new Unstructured **Let's Go** account, at [https://platform.unstructured.io](https://platform.unstructured.io).

   <Note>
     To sign up for a **Business** account instead, [contact Unstructured Sales](https://unstructured.io/?modal=contact-sales), or [learn more](/api-reference/overview#pricing).
   </Note>

2. If you have an Unstructured **Let's Go**, **Pay-As-You-Go**, or **Business SaaS** account and are not already signed in, sign in to your account at [https://platform.unstructured.io](https://platform.unstructured.io).

   <Note>
     For other types of **Business** accounts, see your Unstructured account administrator for sign-in instructions,
     or email Unstructured Support at [support@unstructured.io](mailto:support@unstructured.io).
   </Note>

3. Get your Unstructured API key:<br />

   a. After you sign in to your Unstructured **Let's Go**, **Pay-As-You-Go**, or **Business** account, click **API Keys** on the sidebar.<br />

   <Note>
     For a **Business** account, before you click **API Keys**, make sure you have selected the organizational workspace you want to create an API key
     for. Each API key works with one and only one organizational workspace. [Learn more](/ui/account/workspaces#create-an-api-key-for-a-workspace).
   </Note>

   b. Click **Generate API Key**.<br />
   c. Follow the on-screen instructions to finish generating the key.<br />
   d. Click the **Copy** icon next to your new key to add the key to your system's clipboard. If you lose this key, simply return and click the **Copy** icon again.<br />

### Python toolchain and Claude for Desktop setup

Before you can start coding on your local machine, you must install the Python package and project manager [uv](https://docs.astral.sh/uv/),
and install Claude for Desktop, as follows.

<Steps>
  <Step>
    Install `uv`, as follows.

    By using `pip`:

    ```bash  theme={null}
    pip install uv
    ```

    Or, by using the following script (be sure to restart your terminal or Command Prompt after running the script):

    ```bash  theme={null}
    # For macOS and Linux:
    curl -LsSf https://astral.sh/uv/install.sh | sh

    # For Windows:
    powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
    ```

    (To learn more about `uv`, see the [uv documentation](https://docs.astral.sh/uv/).)
  </Step>

  <Step>
    [Install Claude for Desktop](https://claude.ai/download).
  </Step>
</Steps>

You are now ready to start coding.

## Step 1: Create the MCP server

<Tip>
  If you are not interested in building an MCP server from the beginning and want to use a pre-built MCP server instead,
  skip ahead to [Optional Step 4: Extend the MCP server](#optional-step-4%3A-extend-the-mcp-server).
</Tip>

<Steps>
  <Step>
    From your current working directory, create a project directory and then initialize it. This example creates a
    project directory named `mcp-unstructured-demo`. Then go to the project directory:

    ```bash  theme={null}
    uv init mcp-unstructured-demo
    cd mcp-unstructured-demo
    ```
  </Step>

  <Step>
    Create a virtual environment in the root of the project directory. Then activate the virtual environment:

    ```bash  theme={null}
    # For macOS and Linux:
    uv venv
    source .venv/bin/activate

    # For Windows:
    uv venv
    .venv\Scripts\activate
    ```

    To deactivate and exit the virtual environment at any time, simply run the `deactivate` command:

    ```bash  theme={null}
    deactivate
    ```
  </Step>

  <Step>
    With the virtual environment activated, install the Python SDK for Model Context Protocol and additional code dependencies:

    ```bash  theme={null}
    uv add "mcp[cli]" "unstructured-client>=0.30.6" dotenv
    ```
  </Step>

  <Step>
    In the root of the project directory, create a new Python file. This example names the file `workflow.py`.
    Then add the following code to this new `workflow.py` file:

    ```python  theme={null}
    from typing import Any, AsyncIterator, Optional
    from mcp.server.fastmcp import FastMCP, Context
    from dotenv import load_dotenv
    import os
    from dataclasses import dataclass
    from contextlib import asynccontextmanager
    from unstructured_client import UnstructuredClient
    from unstructured_client.models.operations import (
        ListSourcesRequest,
        GetSourceRequest
    )

    def load_environment_variables() -> None:
        """
        Loads environment variables from a .env file and checks for required variables. 
        Raises an error if any required variable is missing.

        Args:
            None

        Returns:
            None
        """
        load_dotenv()
        required_vars = [
            "UNSTRUCTURED_API_KEY"
        ]

        for var in required_vars:
            if not os.getenv(var):
                raise ValueError(f"Missing required environment variable: {var}")

    @dataclass
    class AppContext:
        client: UnstructuredClient

    @asynccontextmanager
    async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
        """
        Manages application lifecycle with type-safe context
        for the FastMCP server. Initializes the UnstructuredClient.
        
        Args:
            server (FastMCP): The FastMCP server instance.
        
        Returns:
            AsyncIterator[AppContext]: An asynchronous context manager providing the 
            application context.
        """
        api_key = os.getenv("UNSTRUCTURED_API_KEY")
        if not api_key:
            raise ValueError("UNSTRUCTURED_API_KEY environment variable is required")
        client = UnstructuredClient(api_key_auth=api_key)
        try:
            yield AppContext(client=client)
        finally:
            pass

    # Initialize the FastMCP server.
    mcp = FastMCP("workflow", lifespan=app_lifespan)

    @mcp.tool()
    async def list_sources(ctx: Context, source_type: Optional[str] = None) -> str:
        """
        Lists available source connectors. Optionally filters by source type.
        
        Args:
            ctx (Context): The context for the request.
            source_type (Optional[str]): The type of source connector to filter by. 
                If None, all sources are listed.
        
        Returns:
            str: A formatted string listing available source connector names and IDs.
        """
        client = ctx.request_context.lifespan_context.client
        request = ListSourcesRequest()

        if source_type:
            try:
                request.source_type = [source_type]
            except KeyError:
                return f"Invalid source type: {source_type}"

        response = await client.sources.list_sources_async(
            request=request
        )
        sorted_sources = sorted(
            response.response_list_sources, 
            key=lambda source: source.name.lower()
        )
        result = ["Available source connectors:"]

        for source in sorted_sources:
            result.append(f"- {source.name} (ID: {source.id})")

        return "\n".join(result)

    @mcp.tool()
    async def get_source(ctx: Context, source_id: str) -> str:
        """
        Gets detailed information about a specific source connector by its ID.
        
        Args:
            ctx (Context): The context for the request.
            source_id (str): The ID of the source connector to retrieve.
            
        Returns: 
            str: A formatted string containing information about the source connector.
        """
        client = ctx.request_context.lifespan_context.client
        
        response = await client.sources.get_source_async(
            request=GetSourceRequest(source_id=source_id)
        )
        info = response.source_connector_information

        result = ["Source connector information:"]
        result.append(f"- name: {info.name}")
        
        for key, value in info.config:
            result.append(f"{key}: {value}")

        return "\n".join(result)
        
    if __name__ == "__main__":
        load_environment_variables()
        # Initialize and run the MCP server.
        mcp.run(transport='stdio')
    ```
  </Step>

  <Step>
    In the root of the project directory, create a new file named `.env`.
    Then add the following code to this new `.env` file, replacing `<your-unstructured-api-key>` with your Unstructured API key:

    ```text  theme={null}
    UNSTRUCTURED_API_KEY="<your-unstructured-api-key>"
    ```
  </Step>
</Steps>

## Step 2: Configure Claude for Desktop to use the MCP server

<Steps>
  <Step>
    Open the Claude for Desktop App configuration file from the following location, for example by using Visual Studio Code:

    ```text  theme={null}
    # For macOS or Linux:
    code ~/Library/Application\ Support/Claude/claude_desktop_config.json

    # For Windows:
    code $env:AppData\Claude\claude_desktop_config.json
    ```
  </Step>

  <Step>
    Add the following code to the `claude_desktop_config.json` file, and then save the file.

    In this file:

    * `"workflow"` must match the value that you set in `mcp = FastMCP("workflow" ...` in the `workflow.py` file.
    * "Absolute path to" `uv` must be the absolute path to the `uv` executable on your local machine.
    * "Absolute path to project directory" must be the absolute path to the project directory.
    * `mcp-unstructured-demo` must match the name of the project directory that you created.
    * `"workflow.py"` must match the name of the Python file that you created for the MCP server.

    For macOS or Linux:

    ```json  theme={null}
    {
        "mcpServers": {
            "workflow": {
                "command": "/ABSOLUTE/PATH/TO/uv",
                "args": [
                    "--directory",
                    "/ABSOLUTE/PATH/TO/PROJECT/DIRECTORY/mcp-unstructured-demo/",
                    "run",
                    "workflow.py"
                ]
            }
        }
    }
    ```

    For Windows:

    ```json  theme={null}
    {
        "mcpServers": {
            "workflow": {
                "command": "C:\\ABSOLUTE\\PATH\\TO\\uv",
                "args": [
                    "--directory",
                    "C:\\ABSOLUTE\\PATH\\TO\\PROJECT\\DIRECTORY\\mcp-unstructured-demo\\",
                    "run",
                    "workflow.py"
                ]
            }
        }
    }
    ```
  </Step>
</Steps>

## Step 3: Run the MCP server and call it from Claude for Desktop

<Steps>
  <Step>
    Start Claude for Desktop. If it is already running, restart it.
  </Step>

  <Step>
    Ask the following question in the chat window, replacing `S3` with the type of source connector that you want to list.
    For a list of available source connector types, see the [Unstructured API's workflow operations' sources overview](/api-reference/workflow/sources/overview).

    ```text  theme={null}
    List all available source connectors of type S3.
    ```

    Claude for Desktop responds with a list of all available source connectors for that source connector type.
  </Step>

  <Step>
    Ask another question in the chat window, replacing `<source-connector-id>` with one of the source connector IDs that
    Claude for Desktop returned in the previous step. For example:

    ```text  theme={null}
    Provide information about the source connector with ID <source-connector-id>.
    ```

    Claude for Desktop responds with information about that source connector.
  </Step>
</Steps>

## Optional Step 4: Extend the MCP server

Unstructured offers an initial experimental MCP server implementation, with much more functionality then the MCP server that you just created previously in this walkthrough.
This initial MCP server is intended only for experimentation purposes and is not recommended for production usage. A more complete, supported,
production-level implementation is coming soon.

To use Unstructured's initial experimental MCP server implementation, do the following:

<Steps>
  <Step>
    From a different working directory than the one you used in previously in this walkthrough, use Git to clone the
    [Unstructured API MCP Server](https://github.com/Unstructured-IO/UNS-MCP) repository in GitHub, and then switch to the cloned
    repository's root directory:

    ```bash  theme={null}
    git clone https://github.com/Unstructured-IO/UNS-MCP.git
    cd UNS-MCP
    ```
  </Step>

  <Step>
    In the cloned repository's root directory, create a new file named `.env`. Then copy the contents of the repository's
    `.env.template` file into the `.env` file that you just created.

    As an MCP security best practice, sensitive information such as API keys should be stored in environment variables or a
    secrets manager, not hard-coded in MCP server code or retrieved from the user by an MCP client.
    The `.env` file is a common way to store environment variables in Python projects outside of MCP server code and not
    visible to MCP clients.

    Having your own `.env` file helps ensure that you do
    not accidentally commit any sensitive information from this cloned repository into other repositories (as `.env` is an
    entry in this cloned repository's `.gitignore` file, but `.env.template` is not).

    In your `.env` file, replace `<key-here>` in the following line with your Unstructured API key:

    ```text  theme={null}
    UNSTRUCTURED_API_KEY="<key-here>"
    ```

    As needed, replace any of the other placeholders in the `.env` file with your own values. For example, if you want to
    create an Amazon S3 source or destination connector, you should provide your AWS credentials in the `.env` file by specifying values
    for the `AWS_KEY` and `AWS_SECRET` variables. For more information, see the usage comments in the original `.env.template`
    file or in the content that you copied into your `.env` file.

    The original `.env.template` file will be updated as more features are added to the repository. You should periodically
    pull down the source repository's contents into your cloned repository to get any new features, as well as the latest version
    of the `.env.template` file that you can copy any newly available required environment variable names over into your existing `.env` file.
  </Step>

  <Step>
    In the cloned repository's root directory, create a virtual environment. Then activate the virtual environment:

    ```bash  theme={null}
    # For macOS and Linux:
    uv venv
    source .venv/bin/activate

    # For Windows:
    uv venv
    .venv\Scripts\activate
    ```

    To deactivate and exit the virtual environment at any time, simply run the `deactivate` command:

    ```bash  theme={null}
    deactivate
    ```
  </Step>

  <Step>
    With the virtual environment activated, install the cloned repository's code dependencies:

    ```bash  theme={null}
    uv sync
    ```
  </Step>

  <Step>
    Open the Claude for Desktop App configuration file from the following location, for example by using Visual Studio Code:

    ```text  theme={null}
    # For macOS or Linux:
    code ~/Library/Application\ Support/Claude/claude_desktop_config.json

    # For Windows:
    code $env:AppData\Claude\claude_desktop_config.json
    ```
  </Step>

  <Step>
    Overwrite the contents of the `claude_desktop_config.json` file with the following content, and then save the file.

    For macOS or Linux:

    ```json  theme={null}
    {
        "mcpServers": {
            "UNS-MCP": {
                "command": "/ABSOLUTE/PATH/TO/uv",
                "args": [
                    "--directory",
                    "/ABSOLUTE/PATH/TO/PARENT/FOLDER/UNS-MCP",
                    "run",
                    "server.py"
                ]
            }
        }
    }
    ```

    For Windows:

    ```json  theme={null}
    {
        "mcpServers": {
            "workflow": {
                "command": "C:\\ABSOLUTE\\PATH\\TO\\uv",
                "args": [
                    "--directory",
                    "C:\\ABSOLUTE\\PATH\\TO\\PARENT\\FOLDER\\UNS-MCP",
                    "run",
                    "server.py"
                ]
            }
        }
    }
    ```
  </Step>

  <Step>
    Start Claude for Desktop. If it is already running, restart it.
  </Step>

  <Step>
    Here are some questions that you can ask in the chat window to test the MCP server. Replace the placeholders with the appropriate values.

    For a list of available source connector types, see the [Unstructured API's workflow operations sources overview](/api-reference/workflow/sources/overview).

    For a list of available destination connector types, see the [Unstructured API's workflow operations destinations overview](/api-reference/workflow/destinations/overview).

    * `List all available source connectors of type <source-connector-type>.`
    * `Provide information about the source connector with ID <source-connector-id>.`
    * `List all available source connectors of type <destination-connector-type>.`
    * `Provide information about the source connector with ID <destination-connector-id>.`
    * `List all available destination connectors of type <destination-connector-type>.`
    * `Provide information about the destination connector with ID <destination-connector-id>.`
    * `Create a new workflow.`
    * `Provide information about the workflow with ID <workflow-id>.`
    * `Run the workflow with ID <workflow-id>.`
    * `Update the workflow with ID <workflow-id>.`
    * `List all available workflows.`
    * `Delete the worklow with ID <workflow-id>.`
  </Step>

  <Step>
    New features are being added to the source repository over time, which will increase the types of questions that
    you can ask the MCP server. You should periodically
    pull down the source repository's contents into your cloned repository to get any new features as they are added.
  </Step>
</Steps>

## Learn more

* <Icon icon="blog" />  [Building an MCP Server with Unstructured API](https://unstructured.io/blog/building-an-mcp-server-with-unstructured-api)
* <Icon icon="blog" />  [Building an End-to-End Data Pipeline with Custom NER on Unstructured using MCP](https://unstructured.io/blog/building-an-end-to-end-data-pipeline-with-custom-ner-on-unstructured-using-mcp)
* <Icon icon="blog" />  Uses this MCP server: [All Your Unstructured Data in a Databricks Delta Table. Just Say the Word.](https://unstructured.io/blog/all-your-unstructured-data-in-a-databricks-delta-table-just-say-the-word)
