Claude Tools

MCP Servers for Claude: Building File Upload and Processing Tools in Python

Extend Claude AI with custom MCP servers for secure file uploads and processing. Build your own Python tool in this step-by-step guide to supercharge workflows.

J

Jennifer Yu

Workflow Automation Specialist

December 18, 2025 min read
Share:

Why MCP Servers Are a Game-Changer for Claude

Hey Claude fans! If you've been tinkering with Claude's tool-calling features, you know it shines at reasoning and code generation—but what about handling real-world files securely? Enter MCP (Model Context Protocol) servers: lightweight HTTP servers that let Claude offload file uploads, processing, and analysis to custom endpoints. No more clunky workarounds or exposing sensitive data directly to the API.

In this guide, we'll build a Python MCP server using FastAPI for file upload and processing. You'll handle PDFs, CSVs, images—whatever Claude needs—while keeping everything secure and scalable. Perfect for devs automating workflows or teams in HR, legal, or engineering.

By the end, you'll have a running server that Claude can call via tools, complete with examples. Let's dive in!

What is MCP and Why Build Your Own?

MCP is Anthropic's protocol for extending Claude's context with external servers. Claude's computer_use tool (in beta for Opus/Sonnet) or custom tool calls invoke your MCP endpoints over HTTP. It's like giving Claude a "helper bot" for tasks it can't do natively, such as:

  • Uploading files from user prompts
  • Extracting text from PDFs
  • Processing CSVs for insights
  • OCR on images
  • Secure storage/retrieval

Benefits over direct API calls:

  • Security: Validate/authenticate requests server-side.
  • Scalability: Run heavy processing (e.g., via Pandas, PyPDF2) without token limits.
  • Privacy: Keep data on your infra, not Anthropic's.
  • Claude-specific: Integrates seamlessly with Claude's XML tool format.

Compared to generic tools like LangChain, MCP is lightweight and tuned for Claude's ecosystem—no bloat.

Prerequisites

  • Python 3.10+
  • Familiarity with FastAPI (or Flask)
  • Anthropic API key (for testing Claude integration)
  • Libraries: pip install fastapi uvicorn anthropic pypdf2 pandas pillow python-multipart

Quick setup:

mkdir claude-mcp-server && cd claude-mcp-server
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate
pip install fastapi uvicorn anthropic pypdf2 pandas pillow python-multipart

Step 1: MCP Server Skeleton with FastAPI

We'll use FastAPI for its auto-docs, async support, and type hints—ideal for Claude's structured calls.

Create mcp_server.py:

import os
from fastapi import FastAPI, File, UploadFile, HTTPException, Depends
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from pydantic import BaseModel
import uvicorn

app = FastAPI(title="Claude MCP File Server")
security = HTTPBearer()

class MCPRequest(BaseModel):
    action: str  # 'upload', 'process', 'analyze'
    file_id: str | None = None
    params: dict = {}

class MCPResponse(BaseModel):
    success: bool
    data: dict
    error: str | None = None

# In-memory storage (use Redis/DB for prod)
files_store = {}

@app.post("/mcp", response_model=MCPResponse)
async def mcp_endpoint(
    request: MCPRequest,
    creds: HTTPAuthorizationCredentials = Depends(security)
):
    if creds.credentials != "your-secret-token":  # Replace with env var
        raise HTTPException(401, "Unauthorized")
    
    if request.action == "upload":
        return handle_upload(request)
    elif request.action == "process":
        return handle_process(request)
    else:
        raise HTTPException(400, "Invalid action")

def handle_upload(request: MCPRequest) -> MCPResponse:
    # Placeholder - we'll flesh this out
    return MCPResponse(success=True, data={"file_id": "temp_id"})

def handle_process(request: MCPRequest) -> MCPResponse:
    # Placeholder
    return MCPResponse(success=True, data={"result": "processed"})

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

Run it: python mcp_server.py or uvicorn mcp_server:app --reload. Hit http://localhost:8000/docs for Swagger UI.

Step 2: Implement Secure File Upload

Claude will POST files via multipart or base64, but we'll use UploadFile for efficiency.

Update handle_upload:

@app.post("/mcp/upload")
async def upload_file(
    file: UploadFile = File(...),
    creds: HTTPAuthorizationCredentials = Depends(security)
) -> MCPResponse:
    if creds.credentials != os.getenv("MCP_TOKEN"):
        raise HTTPException(401)
    
    if not file.content_type.startswith('application/') and not file.content_type.startswith('image/'):
        raise HTTPException(400, "Unsupported file type")
    
    file_id = f"{hash(file.filename)}_{file.size}"
    content = await file.read()
    files_store[file_id] = {
        "filename": file.filename,
        "content": content,
        "content_type": file.content_type
    }
    
    return MCPResponse(
        success=True,
        data={"file_id": file_id, "size": len(content)}
    )

Security notes:

  • Token auth via header.
  • Size/type limits (add max_length=10*1024*1024 for 10MB).
  • Scan for malware in prod (e.g., ClamAV).

Step 3: File Processing Endpoints

Now, the fun part: Process files Claude requests.

PDF Text Extraction:

from pypdf2 import PdfReader

def extract_pdf_text(file_id: str) -> str:
    if file_id not in files_store:
        raise ValueError("File not found")
    reader = PdfReader(io.BytesIO(files_store[file_id]["content"]))
    return " ".join(page.extract_text() for page in reader.pages)

@app.post("/mcp/process/pdf")
def process_pdf(request: MCPRequest, creds: HTTPAuthorizationCredentials = Depends(security)):
    # Auth check...
    text = extract_pdf_text(request.file_id)
    return MCPResponse(success=True, data={"text": text[:5000]})  # Truncate for tokens

CSV Analysis with Pandas:

import pandas as pd
import io

@app.post("/mcp/process/csv")
def process_csv(request: MCPRequest, creds: HTTPAuthorizationCredentials = Depends(security)):
    df = pd.read_csv(io.BytesIO(files_store[request.file_id]["content"]))
    summary = {
        "shape": df.shape,
        "columns": df.columns.tolist(),
        "stats": df.describe().to_dict(),
        "head": df.head(10).to_dict('records')
    }
    return MCPResponse(success=True, data=summary)

Image OCR (bonus): Use Pillow + pytesseract (pip install pytesseract).

Step 4: Integrate with Claude API

Claude calls your MCP via tools. Define tools in your prompt.

from anthropic import Anthropic

client = Anthropic(api_key="your-key")

tools = [
    {
        "name": "mcp_upload",
        "description": "Upload a file to MCP server",
        "input_schema": {
            "type": "object",
            "properties": {"file_b64": {"type": "string"}, "filename": {"type": "string"}}
        }
    },
    {
        "name": "mcp_process",
        "description": "Process uploaded file",
        "input_schema": {
            "type": "object",
            "properties": {"file_id": {"type": "string"}, "action": {"type": "string", "enum": ["pdf", "csv"]}}
        }
    }
]

response = client.messages.create(
    model="claude-3-5-sonnet-20240620",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "Analyze this PDF: [base64-file-data]"}]
)

# Handle tool calls
for tool in response.stop_reason == "tool_use":
    if tool.name == "mcp_upload":
        # Decode b64, POST to /mcp/upload
        file_id = upload_to_server(tool.input["file_b64"])
        # Feed back to Claude

Full loop: Use client.messages.create iteratively for tool results.

Step 5: Running and Testing

  1. Set export MCP_TOKEN=supersecret.
  2. uvicorn mcp_server:app --port 8000.
  3. Test upload via curl:
curl -X POST "http://localhost:8000/mcp/upload" \
  -H "Authorization: Bearer supersecret" \
  -F "file=@sample.pdf"
  1. Prompt Claude: "Upload and summarize this CSV"—watch it call your server!

Security Best Practices

  • HTTPS only: Use uvicorn with SSL or ngrok.
  • Rate limiting: slowapi middleware.
  • Data retention: Auto-delete files after 1h.
  • Validation: Sanitize inputs, check hashes.
  • Enterprise: Add OAuth/JWT, VPC deployment.
FeatureBasicAdvanced
AuthTokenJWT
StorageMemoryS3/Redis
ProcessingSyncCelery Queue

Advanced: Multi-File Workflows and Agents

Build agentic flows: Claude orchestrates uploads → processes → aggregates.

Example: HR resume screener.

# Agent prompt
"You are a resume analyzer. Steps: 1. Upload resume. 2. Extract text. 3. Score skills. Output JSON."

Scale with Docker:

FROM python:3.11-slim
COPY . .
RUN pip install -r requirements.txt
CMD ["uvicorn", "mcp_server:app", "--host", "0.0.0.0"]

Common Pitfalls and Tips

  • Token bloat: Summarize results before returning to Claude.
  • Async handling: Use BackgroundTasks for long jobs.
  • Error propagation: Claude retries on 5xx—log everything.
  • Comparisons: Beats Zapier for custom logic; pairs with n8n for no-code.

Wrapping Up

Boom! You've got a production-ready MCP server for Claude. Start with uploads, add NLP/computer vision next. Deploy to Vercel/Heroku for teams.

Next reads: Claude Code CLI, API Agents. Questions? Drop a comment!

Word count: ~1450

The #1 Newsletter in AI

Stay ahead of the AI curve

The most important updates, news, and content — delivered in one weekly newsletter.

No spam. Unsubscribe anytime. Privacy policy

MCP
Claude Tools
Python
File Upload
Data Processing
ai-agents
J

About Jennifer Yu

Workflow Automation Specialist

Jennifer covers workflow strategy, no-code platforms, and clear implementation guidance for teams adopting automation.

Comments (0)