ReferencesArcade MCPPython

Resources

resources expose data to clients through URI-addressable endpoints. MCPApp provides build-time methods to register static resources, file-backed resources, and dynamic resource handlers — including URI templates with parameters.

Basic usage

import json

from arcade_mcp_server import MCPApp

app = MCPApp(name="my_server", version="1.0.0")

@app.resource(
    "config://app/settings",
    name="App Settings",
    description="Current application settings",
    mime_type="application/json",
)
def app_settings(uri: str) -> str:
    return json.dumps({"debug": False, "version": "1.0.0"})

if __name__ == "__main__":
    app.run()

Registration methods

resource

@app.resource(
    uri,
    name=None,
    title=None,
    description=None,
    mime_type=None,
    annotations=None,
    meta=None,
)
def handler(uri: str) -> str:
    ...

Decorator for registering a resource handler. If the URI contains template parameters (e.g., {slug}), the resource is registered as a ResourceTemplate and the handler receives the extracted parameters as keyword arguments.

If name is not provided, it defaults to the function name.

Parameters:

NameTypeDescriptionDefault
uristrResource URI or URI templaterequired
namestr | NoneDisplay name for the resourceFunction name
titlestr | NoneHuman-readable titleNone
descriptionstr | NoneResource descriptionNone
mime_typestr | NoneMIME type of the resource contentNone
annotationsAnnotations | NoneResource annotations (audience, priority)None
metadict[str, Any] | NoneArbitrary metadata exposed to clients in _metaNone

add_resource

app.add_resource(
    uri,
    name=None,
    title=None,
    description=None,
    mime_type=None,
    handler=None,
    annotations=None,
    meta=None,
)

Imperative form of @app.resource. Registers a resource or resource template at build time.

Parameters:

NameTypeDescriptionDefault
uristrResource URI or URI templaterequired
namestr | NoneDisplay name for the resourceNone
titlestr | NoneHuman-readable titleNone
descriptionstr | NoneResource descriptionNone
mime_typestr | NoneMIME type of the resource contentNone
handlerCallable[..., Any] | NoneFunction that returns the resource contentNone
annotationsAnnotations | NoneResource annotations (audience, priority)None
metadict[str, Any] | NoneArbitrary metadata exposed to clients in _metaNone

add_text_resource

app.add_text_resource(
    uri,
    text="...",
    name=None,
    title=None,
    description=None,
    mime_type="text/plain",
)

Convenience method for registering a static text resource. No handler function is needed.

Parameters:

NameTypeDescriptionDefault
uristrResource URI (must not be a template)required
textstrThe static text contentrequired
namestr | NoneDisplay name for the resourceNone
titlestr | NoneHuman-readable titleNone
descriptionstr | NoneResource descriptionNone
mime_typestrMIME type of the content'text/plain'

Raises ValueError if uri contains template parameters (e.g., {slug}). Use @app.resource or add_resource for dynamic resources.

add_file_resource

app.add_file_resource(
    uri,
    path="...",
    name=None,
    title=None,
    description=None,
    mime_type=None,
)

Convenience method for registering a file-backed resource. Text files are served as TextResourceContents. Binary files (detected via UnicodeDecodeError) are served as BlobResourceContents with base64 encoding.

Parameters:

NameTypeDescriptionDefault
uristrResource URI (must not be a template)required
pathstr | PathPath to the file on diskrequired
namestr | NoneDisplay name for the resourceNone
titlestr | NoneHuman-readable titleNone
descriptionstr | NoneResource descriptionNone
mime_typestr | NoneMIME type (auto-detected if not provided)None

Raises ValueError if uri contains template parameters (e.g., {slug}). Use @app.resource or add_resource for dynamic resources.

URI templates

URIs that contain {param} or {param*} are registered as resource templates. When a client reads a URI that matches the template, the server extracts the parameters and passes them to the handler as keyword arguments.

Syntax

PatternMatchesExample URIExtracted value
{param}A single path segment (no /)kb://articles/{slug} matching kb://articles/helloslug="hello"
{param*}One or more segments (greedy, crosses /)kb://docs/{path*} matching kb://docs/guides/setuppath="guides/setup"

Template handler signatures

Template handlers receive the full URI as the first argument and the extracted parameters as keyword arguments:

@app.resource("kb://articles/{slug}")
def article_by_slug(uri: str, slug: str) -> str:
    ...

@app.resource("kb://{category}/{slug}")
def article_by_category(uri: str, category: str, slug: str) -> str:
    ...

@app.resource("kb://docs/{path*}")
def docs_tree(uri: str, path: str) -> str:
    ...

Handler return types

Resource handlers can return several types. The server coerces them into the appropriate content type:

Return typeResult
strTextResourceContents
bytesBlobResourceContents (base64-encoded)
dict with "text" keyTextResourceContents
dict with "blob" keyBlobResourceContents
listPassed through as-is (for multiple content items)
OtherTextResourceContents via str()

Async handlers are fully supported:

@app.resource("data://users", mime_type="application/json")
async def list_users(uri: str) -> str:
    users = await fetch_users()
    return json.dumps(users)

Annotations

Use the Annotations class to attach audience and priority metadata to a resource:

from arcade_mcp_server import Annotations, MCPApp

app = MCPApp(name="my_server", version="1.0.0")

@app.resource(
    "kb://announcements/pinned",
    name="Pinned Announcement",
    annotations=Annotations(audience=["user"], priority=1.0),
)
def pinned_announcement(uri: str) -> str:
    return "All-hands meeting this Friday at 3 PM."

Examples

Static text resource

from arcade_mcp_server import MCPApp

app = MCPApp(name="my_server", version="1.0.0")

app.add_text_resource(
    "kb://readme",
    text="Welcome to the Knowledge Base.",
    name="README",
    description="Welcome text for the knowledge base",
)

File-backed resource

from pathlib import Path

from arcade_mcp_server import MCPApp

app = MCPApp(name="my_server", version="1.0.0")

app.add_file_resource(
    "config://pyproject",
    path=Path("pyproject.toml"),
    name="Project Config",
    description="The pyproject.toml for this project",
    mime_type="text/plain",
)

Dynamic resource with URI template

import json

from arcade_mcp_server import MCPApp

app = MCPApp(name="my_server", version="1.0.0")

ARTICLES = {
    "hello-world": {"title": "Hello World", "body": "Welcome!"},
    "getting-started": {"title": "Getting Started", "body": "Let's begin."},
}

@app.resource(
    "kb://articles/index",
    name="Article Index",
    description="List of all articles",
    mime_type="application/json",
)
def article_index(uri: str) -> str:
    return json.dumps(list(ARTICLES.keys()))

@app.resource(
    "kb://articles/{slug}",
    name="Article by Slug",
    description="Retrieve an article by its slug",
    mime_type="application/json",
)
def article_by_slug(uri: str, slug: str) -> str:
    article = ARTICLES.get(slug)
    if not article:
        return json.dumps({"error": "Not found"})
    return json.dumps(article, indent=2)

if __name__ == "__main__":
    app.run()

Resource with custom metadata

from arcade_mcp_server import MCPApp

app = MCPApp(name="my_server", version="1.0.0")

@app.resource(
    "kb://articles/api-guidelines/metadata",
    name="API Guidelines Metadata",
    meta={"tags": ["api", "engineering"], "version": 2, "reviewed": True},
)
def article_metadata(uri: str) -> str:
    return "API guidelines metadata"

Combining tools and resources

import json
from typing import Annotated

from arcade_mcp_server import MCPApp

app = MCPApp(name="my_server", version="1.0.0")

ARTICLES = {
    "hello": {"title": "Hello", "body": "Hello World!"},
}

@app.resource(
    "kb://articles/{slug}",
    name="Article by Slug",
    mime_type="application/json",
)
def get_article_resource(uri: str, slug: str) -> str:
    return json.dumps(ARTICLES.get(slug, {}))

@app.tool
def search_articles(query: Annotated[str, "Search query"]) -> str:
    """Search articles by title."""
    matches = [
        slug for slug, article in ARTICLES.items()
        if query.lower() in article["title"].lower()
    ]
    return json.dumps(matches)

if __name__ == "__main__":
    app.run()
Edit on GitHub

Last updated on

On this page