Add image support to Files and contact photos to Contacts
Files: - read_file returns images inline (jpg/png/gif/webp up to 10MB) - Text files returned as text, binary files as metadata only Contacts: - get_contact includes contact photo as inline image if available - New tool: set_contact_photo (base64 jpeg/png → VCard PHOTO) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+20
-12
@@ -1,12 +1,13 @@
|
||||
"""MCP Files Server — browse and read files via WebDAV/oCIS."""
|
||||
|
||||
import os, sys, contextlib
|
||||
import os, sys, contextlib, base64
|
||||
from xml.etree import ElementTree as ET
|
||||
from typing import Annotated
|
||||
|
||||
import httpx
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from mcp.types import TextContent, ImageContent
|
||||
from starlette.applications import Starlette
|
||||
from starlette.routing import Mount
|
||||
|
||||
@@ -68,20 +69,27 @@ def list_files(
|
||||
return "\n".join(lines) if lines else "Leeres Verzeichnis"
|
||||
|
||||
|
||||
IMAGE_TYPES = {"image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml"}
|
||||
TEXT_HINTS = ["text/", "json", "xml", "csv", "yaml", "javascript", "markdown"]
|
||||
|
||||
@mcp.tool()
|
||||
def read_file(
|
||||
path: Annotated[str, Field(description="Full file path, e.g. '/Documents/notes.txt', '/config.yaml'")],
|
||||
) -> str:
|
||||
"""Read a text file's content (txt, md, json, csv, yaml, xml, html). Binary files show only metadata."""
|
||||
path: Annotated[str, Field(description="Full file path, e.g. '/Documents/notes.txt', '/Photos/pic.jpg'")],
|
||||
) -> list[TextContent | ImageContent]:
|
||||
"""Read a file. Text files return content directly. Images (jpg/png/gif/webp) are displayed inline. Other binary files return only metadata."""
|
||||
user = get_current_user()
|
||||
if not user: return "Error: not authenticated"
|
||||
r = httpx.get(_dav(user, path), auth=_auth(user), timeout=30)
|
||||
if r.status_code >= 400: return f"Fehler: HTTP {r.status_code}"
|
||||
ct = r.headers.get("content-type", "")
|
||||
if any(t in ct for t in ["text/", "json", "xml", "csv", "yaml", "javascript"]):
|
||||
return r.text[:100000]
|
||||
try: return r.content.decode("utf-8")[:100000]
|
||||
except: return f"Binaerdatei ({len(r.content)} bytes, Typ: {ct}). Kann nicht angezeigt werden."
|
||||
if not user: return [TextContent(type="text", text="Error: not authenticated")]
|
||||
r = httpx.get(_dav(user, path), auth=_auth(user), timeout=60)
|
||||
if r.status_code >= 400: return [TextContent(type="text", text=f"Fehler: HTTP {r.status_code}")]
|
||||
ct = r.headers.get("content-type", "").split(";")[0].strip()
|
||||
if ct in IMAGE_TYPES and len(r.content) < 10_000_000:
|
||||
return [ImageContent(type="image", data=base64.b64encode(r.content).decode(), mimeType=ct)]
|
||||
if any(t in ct for t in TEXT_HINTS):
|
||||
return [TextContent(type="text", text=r.text[:100000])]
|
||||
try:
|
||||
return [TextContent(type="text", text=r.content.decode("utf-8")[:100000])]
|
||||
except Exception:
|
||||
return [TextContent(type="text", text=f"Binaerdatei ({len(r.content):,} bytes, Typ: {ct}). Nutze read_file nur fuer Text und Bilder.")]
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
|
||||
Reference in New Issue
Block a user