Skip to content

Commit 38065c8

Browse files
erpicclaude
andcommitted
chore: release 0.8.0
- Regenerate llms-full.txt (now includes the OAuth-MCP and tool-error schema sections from docs/oauth.md and docs/tools.md). - Open a fresh `[Unreleased]` heading in CHANGELOG.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d723640 commit 38065c8

2 files changed

Lines changed: 93 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
66

7+
## [Unreleased]
8+
79
## [0.8.0] - 2026-05-06
810

911
### Added

llms-full.txt

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,13 @@ Tool providers give the LLM callable tools. Extend `BaseToolProvider`:
294294

295295
```python
296296
# my_tools/calculator.py
297-
from slack_agents.tools.base import BaseToolProvider, ToolResult
297+
from slack_agents.tools.base import (
298+
ERROR_INPUT_ERROR,
299+
RECOVERY_ABORT,
300+
BaseToolProvider,
301+
ToolResult,
302+
make_tool_error,
303+
)
298304

299305
class Provider(BaseToolProvider):
300306
def __init__(self, allowed_functions: list[str]):
@@ -320,7 +326,13 @@ class Provider(BaseToolProvider):
320326
if name == "add":
321327
result = arguments["a"] + arguments["b"]
322328
return {"content": str(result), "is_error": False, "files": []}
323-
return {"content": f"Unknown tool: {name}", "is_error": True, "files": []}
329+
return make_tool_error(
330+
error=ERROR_INPUT_ERROR,
331+
code="unknown_tool",
332+
tool=name,
333+
recovery=RECOVERY_ABORT,
334+
message=f"Tool {name!r} is not provided by this calculator.",
335+
)
324336
```
325337

326338
### Key points
@@ -330,6 +342,7 @@ class Provider(BaseToolProvider):
330342
- `call_tool(name, arguments, user_context, storage)` returns a `ToolResult` (`{"content": str, "is_error": bool, "files": list[OutputFile]}`)
331343
- Files in the response are uploaded to Slack automatically
332344
- `initialize()` and `close()` are optional lifecycle hooks
345+
- For error returns, use `make_tool_error(...)` so the LLM gets a structured payload it can interpret consistently. See [Tool error schema](#tool-error-schema) below.
333346

334347
## File Importer Providers
335348

@@ -405,6 +418,82 @@ tools:
405418

406419
All MCP tool providers are initialized in parallel at startup. If any provider fails to connect after exhausting its retries, the agent exits with an error.
407420

421+
## MCP over HTTP with OAuth
422+
423+
`slack_agents.tools.mcp_http_oauth` is the OAuth-authenticated counterpart to
424+
`mcp_http` — for MCP servers that require per-user OAuth 2.1 authentication
425+
rather than a static API key. See [`docs/oauth.md`](oauth.md) for the full
426+
guide. Minimal example:
427+
428+
```yaml
429+
tools:
430+
my-mcp:
431+
type: slack_agents.tools.mcp_http_oauth
432+
url: "https://my-server.example.com/mcp"
433+
allowed_functions: [".*"]
434+
```
435+
436+
This pulls in extra runtime requirements: `OAUTH_PUBLIC_URL`,
437+
`OAUTH_SECRET_KEY`, and an in-process HTTP listener for OAuth callbacks. Read
438+
the OAuth doc before configuring this in production.
439+
440+
## Tool error schema
441+
442+
When a tool returns `is_error: True`, every built-in tool emits a JSON-encoded
443+
payload in `content` matching this shape, so the LLM consuming the result can
444+
reason about the failure uniformly. Custom tools should produce the same shape
445+
via the `make_tool_error` helper.
446+
447+
```json
448+
{
449+
"error": "<type>", // required: e.g. "permission_denied"
450+
"code": "<subtype>", // optional: stable per-error sub-classification
451+
"tool": "<tool name>", // optional: which tool was being called
452+
"server": "<server id>", // optional: provider/server context
453+
"message": "<human>", // required: human-readable summary
454+
"recovery": "<action>", // required: see below
455+
"details": { ... } // optional: free-form per-error-type
456+
}
457+
```
458+
459+
**Top-level error types** (`error`):
460+
461+
| Constant | When to use |
462+
|---|---|
463+
| `ERROR_PERMISSION_DENIED` | Auth/scope/role refusal — user-level |
464+
| `ERROR_SYSTEM_ERROR` | Operational/library/transient failure |
465+
| `ERROR_AUTH_SETUP_FAILED` | Auth flow itself broke (timeout, prompt failure) |
466+
| `ERROR_INPUT_ERROR` | Bad call / unknown tool / bad arguments |
467+
468+
**Recovery actions** (`recovery`):
469+
470+
| Constant | Meaning |
471+
|---|---|
472+
| `RECOVERY_RETRY` | Transient or user-recoverable; just try again |
473+
| `RECOVERY_CONTACT_ADMIN` | Requires realm/IdP/account admin |
474+
| `RECOVERY_CONTACT_SUPPORT` | Framework operator/dev needs to investigate logs |
475+
| `RECOVERY_ABORT` | Nothing to do for this call (LLM may try a different tool) |
476+
477+
**Helper signature:**
478+
479+
```python
480+
from slack_agents.tools.base import make_tool_error # plus ERROR_*, RECOVERY_*
481+
482+
return make_tool_error(
483+
error=ERROR_SYSTEM_ERROR, # required
484+
message="Server returned 502.", # required
485+
recovery=RECOVERY_RETRY, # required
486+
code="upstream_502", # optional
487+
tool="search_docs", # optional
488+
server="my-mcp", # optional
489+
details={"status": 502}, # optional, schema-less
490+
)
491+
```
492+
493+
`details` is intentionally schema-less — each error type can carry whatever
494+
structured fields the LLM benefits from seeing (missing scopes, exception
495+
types, timestamps for support correlation, etc.).
496+
408497
## Configuration
409498

410499
Both types are configured the same way in `config.yaml`:

0 commit comments

Comments
 (0)