Skip to content

Resource Arguments

Capability ai.nimblebrain/resource-arguments, in the client’s capabilities.extensions
Declaration _meta["ai.nimblebrain/resource-arguments"] on a tool in your tools/list result
Delivery _meta["ai.nimblebrain/resource-arguments"] on the tools/call request, one entry per URI
Eras Both. Nothing travels from your server to the host
Schemas Each resolved entry has the shape of the specification’s ReadResourceResult, holding its text contents
Fallback The tool treats the URI as an opaque reference, or asks for the content inline

A tool sometimes needs a resource that is served in its workspace, not one it was handed inline: a record, a document, an entry another server keeps. The host can read it and hand it over with the call. The case that motivates this is a record another server owns.

A server sometimes has to act on a record that another server owns. An outbound tool stores citations such as crm://signals/42, and before it sends anything it has to check the cited record: refuse a citation whose event is too old, or quote its exact wording. Without help from the host it has two options, and neither is acceptable. It can call the other server directly, which couples two services that should know nothing about each other. Or it can trust whatever the agent copied into its arguments, which is exactly what it is trying to verify.

With this extension, your tool declares that an argument holds resource URIs. “Arguments” here means the tools/call arguments, not the URI-template arguments the specification’s completion API refers to. Before the host sends the tools/call, it reads each URI from the server in the same workspace that serves it, and puts the contents in the call’s _meta. Your tool gets the record as its owner returned it, not as the model retold it. The two servers never address each other.

Nothing in this extension is a request from your server to the host, so it works on a 2026-07-28 connection as well as a 2025-era one. That is the difference from host resources, whose methods 2026-07-28 cannot carry.

The host claims the capability on a workspace connector’s connection, on both eras: in initialize on a 2025-era connection, and in every request’s _meta["io.modelcontextprotocol/clientCapabilities"] on 2026-07-28.

{
"capabilities": {
"extensions": {
"ai.nimblebrain/resource-arguments": { "maxChars": 49600, "maxSize": 10485760, "maxUris": 32 }
}
}
}
Field Meaning
maxChars The most characters of text one resolved resource may carry: the limit the agent’s own nb__read_resource applies. A longer resource is refused, never cut.
maxSize The most bytes of resolved text one call carries, all URIs together (10 MiB). Each URI the host reads counts as maxChars × 3 bytes, the most a resource within maxChars can take in UTF-8, so a read that is made is never dropped afterwards.
maxUris The most distinct URIs resolved for one call.

The capability is absent where the host resolves nothing: on a personal connector’s connection, which belongs to a person rather than a workspace, and on any host that does not implement the extension.

Mark the tool in _meta, naming each argument and the URI schemes you accept in it:

{
"name": "send_with_citations",
"description": "Send a message. Pass the source records as `citations`, by their URI.",
"inputSchema": {
"type": "object",
"properties": {
"body": { "type": "string" },
"citations": { "type": "array", "items": { "type": "string" } }
},
"required": ["body"]
},
"_meta": {
"ai.nimblebrain/resource-arguments": {
"arguments": {
"citations": { "schemes": ["crm"] }
}
}
}
}
Field Rule
…arguments Required. An object keyed by top-level argument name.
…arguments.<name>.schemes Required. A non-empty array of URI scheme names, without ://. Matched case-insensitively. There is no wildcard: an entry without a valid scheme declares nothing.

A declared argument is read as a string or an array of strings. Values nested inside an object are not resolved.

The declaration lives on the tool, not inside inputSchema, because the input schema is passed to model providers, and some reject keywords JSON Schema does not define.

When the agent calls the tool, the host puts one entry per distinct string in the declared arguments into the request’s _meta, keyed by the string exactly as it appeared:

{
"method": "tools/call",
"params": {
"name": "send_with_citations",
"arguments": {
"body": "Congratulations on the funding round.",
"citations": ["crm://signals/42", "crm://signals/7", "mail://inbox/1"]
},
"_meta": {
"ai.nimblebrain/resource-arguments": {
"crm://signals/42": {
"contents": [
{
"uri": "crm://signals/42",
"mimeType": "application/json",
"text": "{\"id\":42,\"kind\":\"funding\",\"observedAt\":\"2026-10-01\"}"
}
]
},
"crm://signals/7": {
"error": { "code": "not_found", "message": "No connector in this workspace returned this resource." }
},
"mail://inbox/1": {
"error": { "code": "scheme_not_declared", "message": "This argument does not declare the URI's scheme, so the host did not read it." }
}
}
}
}
}

An entry is one of two shapes:

Shape Meaning
{ "contents": [...] } The serving server’s resources/read result contents, as it returned them. Always text: each entry has uri, text, and mimeType when the server gave one. A resource with any non-text (blob) entry is refused as not_text.
{ "error": { "code", "message" } } The host did not read the URI, or the read gave nothing. See Errors.

A URI is never silently dropped: every string in a declared argument has an entry. Whether an unresolved citation refuses the call is your tool’s decision.

The key is absent when the call has nothing to carry: the tool declares no resource arguments, its declared arguments hold no strings, or the host does not implement the extension. On a call made through the MCP endpoint for an outside client, the request’s client capabilities are that client’s, so check for the _meta key on the call itself rather than relying on the capability.

The host reads a URI from the one server in the calling connection’s workspace whose resources/templates/list has a template the URI expands from (RFC 6570). It is the same rule that finds the app to open a URI in. To be a source for other servers’ tools, publish your resource templates.

  • One workspace. Only the servers installed in the same workspace as your connection are consulted. A server in another workspace is never read, whatever it serves.
  • One owner. When two servers in the workspace claim the URI, the host reads neither and answers ambiguous. It does not guess.
  • The host’s own schemes are not resolved yet. This extension does not yet resolve files://, ui://, skill://, artifact:// or conversation:// URIs, and no server can answer for them by advertising a matching template. For now, workspace files are read with host resources.

Your tool receives only text the agent itself could have read in the same call. Three rules make that true:

  • The same credential. The host sends the read over the serving server’s connection in this workspace, inside the tool call, so the serving server sees exactly what it sees when the agent reads the resource with nb__read_resource. A workspace connector authenticates as the workspace, not as each user, so the read carries the workspace’s credential, not a per-user one.
  • The same reach. The host reads only from servers the agent’s own read reaches. A task run whose list of allowed tools does not include nb__read_resource cannot read connector resources itself, so for a tool it calls the host reads nothing and every URI is not_found.
  • The same shape. The agent’s read gives it text, up to maxChars characters. The host hands on text only (a blob is not_text, and its bytes never reach your tool) and refuses a resource longer than maxChars as too_large rather than cutting it.

Each resolution is recorded on the host’s trace of the tool call: one resource_argument event per URI, with the URI, the serving server, the outcome code, and the bytes handed on. The contents are never recorded.

code Meaning
scheme_not_declared The string’s scheme is not one the argument declares, or the string has no scheme. The host did not read it.
not_found No server in the workspace serves the URI, or its server returned nothing or failed. These are one code on purpose, as in host resources: a read tells your server nothing about what exists beyond the contents it is handed.
ambiguous More than one server in the workspace serves the URI. A workspace admin resolves it by removing one.
not_text The resource’s contents include a non-text (blob) entry. The host hands on text only.
too_large The resource has more than maxChars characters of text. Permanent: the same resource is refused on every call, so drop the citation.
over_limit This call is over its limits: it names more than maxUris distinct URIs with declared schemes, or reading the URI would take the call past maxSize. URIs are admitted in argument order, so the first ones that fit are read; a URI refused here is not read. Retry by splitting the call.
rate_limited Your server’s resource budget in this workspace is spent. Reads made here and host-resources requests share one budget per server per workspace.

The message is for people. Match on code.