Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 144 additions & 15 deletions app/en/references/mcp/python/errors/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ title: "Errors - Arcade MCP Python Reference"
description: "Domain-specific error types raised by the MCP server and components"
---

import { Callout } from "nextra/components";

# Errors

Domain-specific error types raised by the MCP server and components.
Expand Down Expand Up @@ -103,35 +105,162 @@ Error in session management.

Error in transport layer (stdio, HTTP, etc).

## `arcade_core.errors`

Tool execution errors with retry semantics. Re-exported via `arcade_mcp_server.exceptions`.

### `RetryableToolError`

**Bases:** `ToolExecutionError`

The operation failed but can be retried.

```python
from arcade_mcp_server.exceptions import RetryableToolError

# Simple retry
raise RetryableToolError("Service temporarily unavailable")

# With retry delay
raise RetryableToolError(
"Rate limited",
retry_after_ms=5000
)

# With guidance for the AI
raise RetryableToolError(
"Search returned no results",
additional_prompt_content="Try broader search terms or check spelling."
)
```

### `FatalToolError`

**Bases:** `ToolExecutionError`

Unrecoverable error — the AI should not retry.

```python
from arcade_mcp_server.exceptions import FatalToolError

raise FatalToolError("Account has been permanently deleted")

# With developer-only details
raise FatalToolError(
"Configuration error",
developer_message="Missing required API key in environment"
)
```

### `ContextRequiredToolError`

**Bases:** `ToolExecutionError`

The operation needs additional context from the user before it can proceed.

```python
from arcade_mcp_server.exceptions import ContextRequiredToolError

raise ContextRequiredToolError(
"Multiple users found matching 'John'",
additional_prompt_content="Please specify: John Smith ([email protected]) or John Doe ([email protected])"
)
```

### `UpstreamError`

**Bases:** `ToolExecutionError`

Error from an external API or service.

```python
from arcade_mcp_server.exceptions import UpstreamError

raise UpstreamError(
"Slack API error: channel_not_found",
status_code=404
)
```

### `UpstreamRateLimitError`

**Bases:** `UpstreamError`

Rate limit from an external API. Includes retry information.

```python
from arcade_mcp_server.exceptions import UpstreamRateLimitError

raise UpstreamRateLimitError(
"Rate limited by Slack",
retry_after_ms=60_000
)
```

<Callout type="info">
You rarely throw `UpstreamError` manually. Use [error adapters](/references/mcp/python/types#error-adapters) to automatically translate SDK errors.
</Callout>

## Error Hierarchy

```text
MCPError (base)
├── MCPContextError (user/input caused the error)
│ ├── AuthorizationError
│ ├── NotFoundError
│ ├── PromptError
│ └── ResourceError
└── MCPRuntimeError (server/infrastructure caused the error)
├── ProtocolError
├── TransportError
└── ServerError
├── RequestError
│ └── ServerRequestError
├── ResponseError
├── SessionError
└── LifespanError

ToolkitError (base for tool errors)
└── ToolError
└── ToolRuntimeError
└── ToolExecutionError
├── RetryableToolError (can retry)
├── FatalToolError (do not retry)
├── ContextRequiredToolError (needs user input)
└── UpstreamError (external API failure)
└── UpstreamRateLimitError
```

## Examples

### Raising exceptions for common error scenarios

```python
from arcade_mcp_server.exceptions import (
MCPError,
NotFoundError,
ValidationError,
ToolError,
RetryableToolError,
FatalToolError,
)

# Raising a not-found when a resource is missing
# Resource not found
async def read_resource_or_fail(uri: str) -> str:
if not await exists(uri):
raise NotFoundError(f"Resource not found: {uri}")
return await read(uri)

# Validating input
def validate_age(age: int) -> None:
if age < 0:
raise ValidationError("age must be non-negative")

# Handling tool execution errors in middleware or handlers
async def call_tool_safely(call):
# Retryable failure
async def fetch_data(url: str):
try:
return await call()
except ToolError as e:
# Convert to an error result or re-raise
raise MCPError(f"Tool failed: {e}")
return await http_client.get(url)
except ConnectionError as e:
raise RetryableToolError(
"Failed to connect. Please try again.",
developer_message=str(e)
)

# Unrecoverable failure
def validate_config(config: dict):
if "api_key" not in config:
raise FatalToolError("Missing required configuration: api_key")
```

78 changes: 78 additions & 0 deletions app/en/references/mcp/python/overview/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,84 @@ Decorator for adding tools with optional parameters.

#### Examples

##### With OAuth Authentication

Use `requires_auth` when your tool needs to act on behalf of a user. Add a `context: Context` parameter to access the authorization token.

```python
from arcade_mcp_server import MCPApp, Context
from arcade_mcp_server.auth import Google
from typing import Annotated

app = MCPApp(name="my_server")

@app.tool(requires_auth=Google(scopes=["profile", "email"]))
async def get_profile(
user_id: Annotated[str, "The user ID to look up"],
context: Context,
) -> dict:
"""Get user profile from Google."""
token = context.authorization.token # OAuth access token
# Use token to call Google APIs...
return {"user_id": user_id}
```

Available auth providers:

```python
from arcade_mcp_server.auth import (
Google,
GitHub,
Slack,
Microsoft,
Discord,
LinkedIn,
Dropbox,
# ... and more
)
```

##### With Secrets

Use `requires_secrets` for API keys your server needs. Add a `context: Context` parameter to access secrets.

```python
from arcade_mcp_server import MCPApp, Context
from typing import Annotated

app = MCPApp(name="my_server")

@app.tool(requires_secrets=["API_KEY"])
async def call_external_api(
query: Annotated[str, "Search query"],
context: Context,
) -> str:
"""Call an external API."""
api_key = context.get_secret("API_KEY")
# Use api_key to call external service...
return f"Results for: {query}"
```

##### With Lifecycle Hooks

Use `@app.on_event` for startup/shutdown logic.

```python
from arcade_mcp_server import MCPApp

app = MCPApp(name="my_server")

@app.on_event("startup")
async def on_startup():
await db.connect()

@app.on_event("shutdown")
async def on_shutdown():
await db.disconnect()
```

##### Full Example

```python
# --- server.py ---
# Programmatic server creation with a simple tool and HTTP transport
Expand Down
17 changes: 13 additions & 4 deletions app/en/references/mcp/python/transports/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,19 @@ app.run(transport="http", host="0.0.0.0", port=8080)

When running in HTTP mode, the server provides:

- `GET /worker/health` - Health check endpoint
- `GET /mcp` - SSE endpoint for MCP protocol
- `GET /docs` - Swagger UI documentation (debug mode)
- `GET /redoc` - ReDoc documentation (debug mode)
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/worker/health` | GET | Health check (returns 200 OK) |
| `/mcp` | GET | SSE stream for server-initiated messages |
| `/mcp` | POST | Send JSON-RPC message |
| `/mcp` | DELETE | Terminate session (when using session IDs) |
| `/docs` | GET | Swagger UI documentation (debug mode) |
| `/redoc` | GET | ReDoc documentation (debug mode) |

<Callout type="info">
POST requests may return `application/json` (single response) or `text/event-stream` (SSE stream).
Clients should include `Accept: application/json, text/event-stream` in requests.
</Callout>

### Development Features

Expand Down
57 changes: 57 additions & 0 deletions app/en/references/mcp/python/types/page.mdx
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
title: "Types - Arcade MCP Python Reference"
description: "Core types, models, and error adapters for MCP servers"
---

# Types

Core Pydantic models and enums for the MCP protocol shapes.
Expand Down Expand Up @@ -54,3 +59,55 @@ result = CallToolResult(
)
```

## Error Adapters

Error adapters translate vendor-specific exceptions into Arcade errors automatically.

### `arcade_tdk.error_adapters`

#### `ErrorAdapter`

**Type:** Protocol

Base interface for error adapters.

```python
class ErrorAdapter(Protocol):
slug: str
def from_exception(self, error: Exception) -> MCPError | None: ...
```

### Built-in Adapters

| Adapter | Handles |
|---------|---------|
| `SlackErrorAdapter` | Slack SDK errors |
| `GoogleErrorAdapter` | Google API Client errors |
| `MicrosoftGraphErrorAdapter` | Microsoft Graph SDK errors |
| `HTTPErrorAdapter` | fetch/HTTP library errors |
| `GraphQLErrorAdapter` | GraphQL client errors |

### Using Error Adapters

Pass adapters to `@app.tool` to automatically translate exceptions:

```python
from arcade_mcp_server import MCPApp
from arcade_tdk.error_adapters import SlackErrorAdapter
from typing import Annotated

app = MCPApp(name="slack_bot")

@app.tool(adapters=[SlackErrorAdapter()])
def send_message(
channel: Annotated[str, "Slack channel ID"],
text: Annotated[str, "Message text"]
) -> str:
"""Send a message to Slack."""
# SlackApiError → UpstreamError automatically
slack_client.chat_postMessage(channel=channel, text=text)
return "Sent!"
```

Adapters are tried in order. First match wins. `HTTPErrorAdapter` is always added as fallback.