Document MCP resources and prompts support
- MCP-Servers: new 'Interacting with MCP Servers' section covering the per-server meta-functions, the unified catalog (kind-aware search and describe, audience annotations), capability gating, mcp_read paging, pattern filtering and template expansion, blob sniff/spill behavior with size ceilings and eviction, bounded tool results, and prompts - REPL: .prompt command (usage, interactive args, result-as-chat-input, staged live tab-completion with silent-empty semantics, macro shadowing), .temp-role rename note, .list prompts row, completion bullet - Roles: temporary roles now use .temp-role (REPL) / --prompt (CLI) - Tools: MCP tool result bounding note and sanitized terminal error text - Home: MCP quick-link mentions resources and prompts - Sidebar: anchors for the new MCP sections
+1
-1
@@ -35,7 +35,7 @@ Coming from [AIChat](https://github.com/sigoden/aichat)? Follow the [migration g
|
||||
* [Create Custom TypeScript Tools](Custom-Tools#custom-typescript-based-tools)
|
||||
* [Create Custom Bash Tools](Custom-Bash-Tools)
|
||||
* [Bash Prompt Utilities](Bash-Prompt-Helpers)
|
||||
* [First-Class MCP Server Support](MCP-Servers): Easily connect and interact with MCP servers for advanced functionality.
|
||||
* [First-Class MCP Server Support](MCP-Servers): Easily connect and interact with MCP servers for advanced functionality. Coyote supports all three MCP capabilities: tools, resources, and prompts, with capability-gated meta-tools for the model and a `.prompt` REPL command for invoking server prompts yourself.
|
||||
* [Macros](Macros): Automate repetitive tasks and workflows with Coyote "scripts" (macros), and invoke them as your own custom REPL commands.
|
||||
* [RAG](RAG): Retrieval-Augmented Generation for enhanced information retrieval and generation.
|
||||
* [Sessions](Sessions): Manage and persist conversational contexts and settings across multiple interactions.
|
||||
|
||||
+140
@@ -544,6 +544,143 @@ To pick up new default servers added in a Coyote update, run `coyote --install m
|
||||
in the REPL). This **merges** the bundled template into your existing configuration: only servers not already present
|
||||
in your file are added; your existing servers and any custom secret references are left untouched.
|
||||
|
||||
# Interacting with MCP Servers
|
||||
|
||||
Coyote does not flood the model's context with a separate function declaration for every tool an MCP server exposes.
|
||||
Instead, each enabled server contributes a small set of **meta-functions** the model uses to discover and invoke the
|
||||
server's capabilities on demand:
|
||||
|
||||
| Meta-function | Purpose |
|
||||
|-------------------------|------------------------------------------------------------------------------------------------|
|
||||
| `mcp_search_<server>` | Keyword search over the server's catalog of tools, resources, resource templates, and prompts |
|
||||
| `mcp_describe_<server>` | Fetch the full schema or metadata for exactly one catalog item |
|
||||
| `mcp_invoke_<server>` | Call a tool on the server |
|
||||
| `mcp_read_<server>` | Read a resource (or expand a resource template) from the server |
|
||||
| `mcp_prompt_<server>` | Fetch a server-defined prompt, rendered with the given arguments |
|
||||
|
||||
This keeps the per-server context cost small and constant no matter how large the server is: the model searches for
|
||||
what it needs, describes the matching item to get its exact schema, and then invokes, reads, or fetches it.
|
||||
|
||||
## The Unified Catalog
|
||||
|
||||
`mcp_search_<server>` searches a single catalog spanning everything the server advertises: tools, resources, resource
|
||||
templates, and prompts. Every search result carries a `kind` field (`tool`, `resource`, `resource_template`, or
|
||||
`prompt`) so the model knows whether to follow up with `mcp_invoke`, `mcp_read`, or `mcp_prompt`. Listings follow
|
||||
server-side pagination, so large servers are cataloged completely.
|
||||
|
||||
`mcp_describe_<server>` accepts the same `kind` as an optional parameter (default: `tool`). The `tool` parameter
|
||||
carries the identifier for every kind: a tool name, a resource URI, a template's URI template, or a prompt name.
|
||||
|
||||
* `kind: "tool"` returns the tool's full invocation schema
|
||||
* `kind: "resource"` returns the resource's catalog metadata (URI, MIME type, size)
|
||||
* `kind: "resource_template"` returns the template and its variables
|
||||
* `kind: "prompt"` returns the prompt's name, description, and arguments
|
||||
|
||||
Servers can annotate resources with an intended `audience` (`user` and/or `assistant`). Per the MCP spec this is
|
||||
advisory metadata, not access control, and Coyote surfaces it on both search results and read results so the model
|
||||
(and you) can see who the content was meant for.
|
||||
|
||||
## Capability Gating
|
||||
|
||||
Servers advertise which capabilities they support (tools, resources, prompts) during the connection handshake, and
|
||||
Coyote only emits the meta-functions that make sense for each server:
|
||||
|
||||
| Meta-function | Emitted when... |
|
||||
|----------------|------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `mcp_search` | Always (the catalog degrades per kind, so search works with whatever the server supports) |
|
||||
| `mcp_describe` | Always (same reason) |
|
||||
| `mcp_invoke` | The server advertises tools, **or** the handshake info is unavailable (fail-open: a handshake hiccup never strips a working server's tools) |
|
||||
| `mcp_read` | The server advertises resources |
|
||||
| `mcp_prompt` | The server advertises prompts |
|
||||
|
||||
So a resources-only server still gets `mcp_search`, `mcp_describe`, and `mcp_read`, and never wastes context on an
|
||||
`mcp_invoke` or `mcp_prompt` that would always error.
|
||||
|
||||
## Reading Resources
|
||||
|
||||
`mcp_read_<server>` reads a resource by URI, or expands a resource template with variable values:
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|-------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `uri` | yes | The resource URI, or a resource template with `{var}` placeholders |
|
||||
| `arguments` | no | Template variable values. Only [RFC 6570](https://www.rfc-editor.org/rfc/rfc6570) Level 1 simple substitution is supported; values are percent-encoded |
|
||||
| `pattern` | no | A regex applied to text content: only matching lines are returned, grep-style, with 2 lines of context and line-number prefixes |
|
||||
| `offset` | no | Byte offset for paging text (default: `0`). When `pattern` is set, offsets refer to the **filtered** stream, not the raw resource |
|
||||
| `max_bytes` | no | Maximum text bytes to return (default: `51200`, clamped to `204800`) |
|
||||
|
||||
Text content comes back as a structured result with paging metadata:
|
||||
|
||||
```json
|
||||
{
|
||||
"uri": "file:///var/log/app.log",
|
||||
"mime_type": "text/plain",
|
||||
"text": "...",
|
||||
"truncated": true,
|
||||
"total_bytes": 1048576,
|
||||
"next_offset": 51200,
|
||||
"note": "Content truncated; re-call with offset=51200 to continue (max_bytes is clamped to 204800)"
|
||||
}
|
||||
```
|
||||
|
||||
The `pattern` filter is applied before slicing, so paging walks the filtered stream: `total_bytes` and `next_offset`
|
||||
describe the filtered content, and for a multi-megabyte log resource, "lines matching ERROR" is one call instead of
|
||||
dozens of pages. When a read returns multiple content items, the paging parameters apply per text item and the whole
|
||||
response is additionally capped at 204800 bytes; items beyond the cap are replaced with a marker naming how many were
|
||||
omitted.
|
||||
|
||||
### Binary Content
|
||||
|
||||
Binary content is **never** inlined into model context. Instead:
|
||||
|
||||
1. Coyote first attempts a UTF-8 decode of the blob. If it decodes cleanly, it is treated as text and paged inline
|
||||
regardless of the server's claimed MIME type (servers mislabel text constantly).
|
||||
2. Genuinely binary content is decoded (up to a 50 MiB ceiling) and written to
|
||||
`<cache_dir>/mcp-resources/<server>/<sha256>.<ext>` with `0600` permissions, and the read returns a
|
||||
self-describing metadata object instead of the bytes:
|
||||
|
||||
```json
|
||||
{
|
||||
"spilled": true,
|
||||
"path": "/home/you/.cache/coyote/mcp-resources/github/3b0c44...98fb.png",
|
||||
"uri": "resource://github/chart",
|
||||
"mime_type": "image/png",
|
||||
"sniffed": false,
|
||||
"size_bytes": 204812,
|
||||
"sha256": "3b0c44...98fb"
|
||||
}
|
||||
```
|
||||
|
||||
The path is actionable by you, by `execute_command`, by the `fs_*` tools, and by sub-agents, which is more useful
|
||||
than base64 the model cannot act on anyway. The spill directory is bounded: when the `mcp-resources/` tree exceeds
|
||||
512 MiB, the oldest files are evicted best-effort. Files are content-addressed by SHA-256, so re-reading the same
|
||||
content regenerates the identical path. Spilled files are untrusted input: Coyote never executes or auto-opens them.
|
||||
|
||||
## Bounded Tool Results
|
||||
|
||||
The same content policy bounds `mcp_invoke` tool results before they reach the model:
|
||||
|
||||
* Text content larger than 204800 bytes is sliced at a UTF-8 boundary with a truncation note asking the model to
|
||||
re-call with narrower arguments.
|
||||
* Image, audio, and embedded binary content is routed through the same spill pipeline as resource reads: written to
|
||||
disk, with the metadata object returned in its place.
|
||||
* Oversized structured content (beyond the same 204800-byte ceiling once serialized) is replaced with a truncation
|
||||
marker.
|
||||
* Server-supplied metadata strings (URIs, MIME types, and similar) are clamped to 4096 bytes.
|
||||
|
||||
In-bounds results pass through byte-identical, so well-behaved servers are unaffected.
|
||||
|
||||
## Prompts
|
||||
|
||||
Servers that advertise the prompts capability expose named, parameterized message templates rendered server-side.
|
||||
Coyote surfaces them in two places:
|
||||
|
||||
* **For you:** the [`.prompt` REPL command](REPL#prompt---invoke-an-mcp-server-prompt) invokes a prompt and submits
|
||||
the result as your chat input, and `.list prompts` lists every prompt across enabled servers.
|
||||
* **For the model:** the `mcp_prompt_<server>` meta-function takes a `prompt` name and an optional `arguments` object
|
||||
(string values only; prompt arguments have no schemas per the MCP spec) and returns the rendered prompt text as the
|
||||
tool result. Multi-message prompts are flattened into a single block with `[user]` / `[assistant]` labels, and
|
||||
missing required arguments produce an error listing them.
|
||||
|
||||
# Coyote Configuration
|
||||
MCP servers, like tools, can be used in a handful of contexts:
|
||||
* Inside a session
|
||||
@@ -553,6 +690,9 @@ MCP servers, like tools, can be used in a handful of contexts:
|
||||
|
||||
Each of these has a different configuration and interaction with the global configuration.
|
||||
|
||||
Enabling a server in any of these contexts exposes its capability-gated meta-functions to the model; see
|
||||
[Interacting with MCP Servers](#interacting-with-mcp-servers) above.
|
||||
|
||||
***Note:** The names of each MCP server referenced in the below configuration properties directly corresponds
|
||||
to the names given in the `functions/mcp.json` configuration file. So if you change the name of an MCP server
|
||||
from `slack` to `lucem-slack`, then you need to also update your Coyote configuration accordingly.
|
||||
|
||||
+56
-3
@@ -18,6 +18,8 @@ things like
|
||||
* `.set key <tab>` to complete configuration values
|
||||
* `.mcp auth <tab>` to complete remote MCP server names
|
||||
* `.macro <tab>` to complete macro names and the `enable`/`disable` subcommands
|
||||
* `.prompt <tab>` to complete MCP servers, then prompt names, then `key=` arguments, queried live from the
|
||||
running servers (see [`.prompt`](#prompt---invoke-an-mcp-server-prompt) below)
|
||||
* **Multi-Line Prompts:** You can also type prompts that span more than one line to help organize your thoughts. This
|
||||
can be done in the following ways:
|
||||
* `Ctrl-o` to open the current input buffer in your preferred editor (either the value of `editor` or `$EDITOR`)
|
||||
@@ -71,12 +73,62 @@ Coyote offers the following commands to manage your roles:
|
||||
|
||||
For more information about roles in Coyote and how to build them, refer to the [roles documentation](Roles).
|
||||
|
||||
## `.prompt` - Set a temporary role using a prompt
|
||||
If you need to create a temporary role that you want to discard after use, you use `.prompt`. `.prompt`-based roles
|
||||
cannot be persisted to a file and saved.
|
||||
## `.temp-role` - Set a temporary role using a prompt
|
||||
If you need to create a temporary role that you want to discard after use, you use `.temp-role`. `.temp-role`-based
|
||||
roles cannot be persisted to a file and saved.
|
||||
|
||||
> **Renamed from `.prompt`:** this command used to be called `.prompt`. That name now invokes MCP server prompts
|
||||
> (see [`.prompt`](#prompt---invoke-an-mcp-server-prompt) below). If you have muscle memory or macros built around
|
||||
> `.prompt <text>`, update them to `.temp-role <text>`.
|
||||
|
||||

|
||||
|
||||
## `.prompt` - Invoke an MCP server prompt
|
||||
MCP servers can expose [prompts](MCP-Servers#prompts): named, parameterized message templates that are rendered
|
||||
server-side, so the server can embed live data the client never sees. `.prompt` invokes one and submits the rendered
|
||||
result as your chat input:
|
||||
|
||||
```
|
||||
.prompt <server> <name> [key=value ...]
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
.prompt github summarize_pr repo=coyote number=42
|
||||
```
|
||||
|
||||
* **Arguments are named**, matching the prompt's declared argument names (MCP prompt arguments have no positional
|
||||
order). Values containing spaces can be quoted: `key="some value"`.
|
||||
* **Missing required arguments are prompted for interactively**, one at a time, before the prompt is fetched.
|
||||
* **The result is submitted as your chat input**, exactly as if you had typed it. Multi-message prompts are flattened
|
||||
into a single user message with `[user]` / `[assistant]` labels marking each original message's role. The flattened
|
||||
text is never parsed as a REPL command or shell line, so prompt content beginning with `.` or `!` is chatted
|
||||
verbatim, not executed.
|
||||
* **Authentication errors surface here.** If the server's token has expired (or it was never authenticated),
|
||||
`.prompt` reports the auth-required error the same way `mcp_invoke` does; run `.mcp auth <server>` to recover.
|
||||
|
||||
Discover available prompts with `.list prompts`, which prints a `server / name / description / args` table across
|
||||
all enabled servers.
|
||||
|
||||
**Tab completion** is staged and queries the running MCP servers live on every TAB press (bounded by a 2-second
|
||||
timeout per query):
|
||||
|
||||
| Input | Suggestions |
|
||||
|----------------------------------|-----------------------------------------------------------------------------------------------|
|
||||
| `.prompt <TAB>` | Enabled, running servers that advertise the prompts capability |
|
||||
| `.prompt <server> <TAB>` | That server's prompt names, with descriptions |
|
||||
| `.prompt <server> <name> <TAB>` | `key=` suggestions for the prompt's arguments, with required ones marked; keys you've already typed are excluded |
|
||||
|
||||
An empty completion list is silent by design: it means the server is not running or authenticated, or has no
|
||||
prompts, and is not a bug. Completion never starts a server and never triggers an authentication flow; an
|
||||
enabled-but-unauthenticated (or stopped) server simply shows nothing at TAB. Auth recovery happens at invocation,
|
||||
where `.prompt` surfaces the actual error.
|
||||
|
||||
> **Macro shadowing:** built-in commands win name collisions, so a user macro named `prompt` is shadowed by this
|
||||
> command. `.list macros` marks it `shadowed (built-in)`, and you can still run it explicitly with
|
||||
> `.macro prompt [args...]`.
|
||||
|
||||
## `.skill` - Skill management
|
||||
Skills are modular knowledge or capability packs the LLM can load and unload mid-conversation. Multiple skills can be
|
||||
loaded at once; their instructions stack and their tools/MCP servers union with the active role/agent/session.
|
||||
@@ -400,6 +452,7 @@ The `.list` command lists the assets of a given kind, making them discoverable w
|
||||
| `.list macros` | List all macros |
|
||||
| `.list bundles` | List installed bundles with their source, version, ref pin, and drift status (see [Sharing Configurations](Sharing-Configurations)) |
|
||||
| `.list skills` | List skills available in this context, with descriptions and a `(loaded)` marker for active skills |
|
||||
| `.list prompts` | List MCP prompts across all enabled servers (live listing), with each prompt's server, description, and arguments |
|
||||
| `.list tools` | List the tools that can be enabled/disabled via `.tool [enable\|disable] <name>` (excludes internal tools; in an agent context, lists the agent's tool pool) |
|
||||
| `.list mcp-servers` | List the MCP servers that can be enabled/disabled via `.mcp [enable\|disable] <name>` (configured servers plus mapping aliases) |
|
||||
|
||||
|
||||
+3
-2
@@ -258,7 +258,8 @@ for more examples.
|
||||
* `slack`: Interact with Slack using natural language
|
||||
|
||||
# Temporary Roles
|
||||
Coyote also enables you to create temporary roles that will be discarded once you're finished with them. This is done via
|
||||
the `.prompt/--prompt` command:
|
||||
Coyote also enables you to create temporary roles that will be discarded once you're finished with them. This is done via
|
||||
the `.temp-role` REPL command (previously named `.prompt`; that name now invokes [MCP server prompts](REPL#prompt---invoke-an-mcp-server-prompt))
|
||||
or the `--prompt` CLI flag:
|
||||
|
||||

|
||||
|
||||
+9
@@ -204,6 +204,15 @@ the model receives:
|
||||
This allows the model to understand that an external service failed and take appropriate action (retry, use an
|
||||
alternative approach, or inform the user).
|
||||
|
||||
MCP error text shown in your terminal is sanitized (escape sequences are stripped so a misbehaving server cannot
|
||||
drive your terminal); the JSON payload the model receives keeps the raw message.
|
||||
|
||||
## MCP Tool Result Bounding
|
||||
MCP tool results are bounded before they reach the model: text larger than 200 KiB is sliced with a truncation
|
||||
marker, images and binary blobs are written to disk (with a metadata object returned in their place) instead of
|
||||
being inlined as base64, and oversized structured content is replaced with a truncation marker. In-bounds results
|
||||
pass through unchanged. See [MCP Servers: Bounded Tool Results](MCP-Servers#bounded-tool-results) for details.
|
||||
|
||||
## Why This Matters
|
||||
Without proper error propagation, models would only know that "something went wrong" without understanding *what*
|
||||
went wrong. By including stderr output and detailed error messages, models can:
|
||||
|
||||
+3
@@ -36,6 +36,9 @@
|
||||
- [Managing from CLI](MCP-Servers#managing-mcp-servers-from-the-cli)
|
||||
- [Workspace-Local Servers](MCP-Servers#workspace-local-mcp-servers)
|
||||
- [OAuth Authentication](MCP-Servers#oauth-authentication)
|
||||
- [Interacting with MCP Servers](MCP-Servers#interacting-with-mcp-servers)
|
||||
- [Reading Resources](MCP-Servers#reading-resources)
|
||||
- [Prompts](MCP-Servers#prompts)
|
||||
- [Sandbox Compatibility](MCP-Servers#sandbox-compatibility)
|
||||
|
||||
## Agents
|
||||
|
||||
Reference in New Issue
Block a user