@frontmcp/skills 1.8.4 → 1.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/catalog/frontmcp-channels/references/channel-sources.md +7 -1
- package/catalog/frontmcp-config/examples/configure-auth-modes/local-behind-tunnel.md +8 -7
- package/catalog/frontmcp-config/references/configure-auth-modes.md +2 -0
- package/catalog/frontmcp-config/references/configure-auth.md +3 -1
- package/catalog/frontmcp-deployment/references/build-for-browser.md +2 -0
- package/catalog/frontmcp-deployment/references/deploy-to-cloudflare-skills-only.md +10 -0
- package/catalog/frontmcp-deployment/references/deploy-to-cloudflare.md +13 -5
- package/catalog/frontmcp-deployment/references/protocol-versions.md +1 -1
- package/catalog/frontmcp-development/references/create-agent.md +4 -0
- package/catalog/frontmcp-development/references/create-skill-with-tools.md +2 -0
- package/catalog/frontmcp-development/references/create-skill.md +3 -1
- package/catalog/frontmcp-development/references/official-plugins.md +1 -0
- package/catalog/frontmcp-development/references/openapi-adapter.md +7 -7
- package/catalog/skills-manifest.json +1 -1
- package/package.json +1 -1
|
@@ -37,6 +37,8 @@ class CIAlertChannel extends ChannelContext {
|
|
|
37
37
|
}
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
The path is a `POST` route on FrontMCP's HTTP server (`bootstrap()` / `createHandler()`), guarded like a custom `http.routes` entry: `throttle.ipFilter` runs first, the path may not be a FrontMCP path (MCP endpoint, `/oauth/*`, `/.well-known/*`, `/health`, `/metrics`), and one channel per path (either mistake fails startup). The route does not authenticate the sender: verify signatures (e.g. `X-Hub-Signature-256`) in `onEvent()`. `createDirect()`, stdio and `createFetchHandler()` serve no webhook route.
|
|
41
|
+
|
|
40
42
|
## App Event Source
|
|
41
43
|
|
|
42
44
|
Subscribes to the in-process `ChannelEventBus`. Your application code emits events, and the channel transforms them into notifications.
|
|
@@ -69,6 +71,8 @@ scope.channelEventBus.emit('app:error', {
|
|
|
69
71
|
|
|
70
72
|
Automatically pushes when registered agents finish execution. Optionally filter by agent IDs.
|
|
71
73
|
|
|
74
|
+
An event is published once an `invoke_<agent>` call has finished: `status: 'success'` only when the agent's output also passed its `outputSchema` (output that fails it is an `'error'`), and a call waiting for the client's answer to an elicitation publishes nothing until it finishes.
|
|
75
|
+
|
|
72
76
|
```typescript
|
|
73
77
|
@Channel({
|
|
74
78
|
name: 'agent-done',
|
|
@@ -96,9 +100,11 @@ class AgentDoneChannel extends ChannelContext {
|
|
|
96
100
|
}
|
|
97
101
|
```
|
|
98
102
|
|
|
103
|
+
The event is `{ agentId, agentName, status: 'success' | 'error', durationMs, output?, error?, runId?, sessionId }`, published after every `invoke_<agent>` call and delivered only to the session that called the agent.
|
|
104
|
+
|
|
99
105
|
## Job Completion Source
|
|
100
106
|
|
|
101
|
-
Pushes when background jobs or workflows complete. Optionally filter by job names.
|
|
107
|
+
Pushes when background jobs or workflows complete. Optionally filter by job names. The event is `{ jobName, jobId, status: 'success' | 'error', durationMs?, output?, error?, attempt, sessionId }` (for a workflow, `jobName` is its name). It goes only to the session that ran the job; a run with no session is delivered to no one.
|
|
102
108
|
|
|
103
109
|
```typescript
|
|
104
110
|
@Channel({
|
|
@@ -7,7 +7,7 @@ tags: [config, auth, local, tunnel, proxy, issuer, auth-modes]
|
|
|
7
7
|
features:
|
|
8
8
|
- 'Relying on request-host-derived OAuth discovery, which works behind a tunnel or under an http.entryPath without extra config'
|
|
9
9
|
- 'Setting `local.issuer` to a full public HTTPS URL so the token `iss` matches what clients reach through the proxy'
|
|
10
|
-
- 'Knowing `
|
|
10
|
+
- 'Knowing the issuer order: `local.issuer`, then `FRONTMCP_PUBLIC_URL`, then `FRONTMCP_PUBLIC_HOST` (host only), then the request'
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
# Local Mode Behind a Tunnel
|
|
@@ -20,12 +20,13 @@ Expose a local-mode server through a tunnel or TLS proxy by aligning the token i
|
|
|
20
20
|
// src/server.ts
|
|
21
21
|
// OAuth discovery (.well-known/*) is derived from the incoming request host at
|
|
22
22
|
// runtime and advertises /oauth/* at the root, so it works behind a tunnel or
|
|
23
|
-
// reverse proxy with no extra config.
|
|
24
|
-
//
|
|
23
|
+
// reverse proxy with no extra config. The issuer is the same in discovery, on
|
|
24
|
+
// authorization responses (RFC 9207 `iss`) and in tokens: `local.issuer`, else
|
|
25
|
+
// FRONTMCP_PUBLIC_URL, else FRONTMCP_PUBLIC_HOST, else the request's origin.
|
|
25
26
|
//
|
|
26
|
-
// `FRONTMCP_PUBLIC_HOST=mcp.example.com` would set only the
|
|
27
|
-
//
|
|
28
|
-
//
|
|
27
|
+
// `FRONTMCP_PUBLIC_HOST=mcp.example.com` would set only the HOST (scheme stays
|
|
28
|
+
// http, port stays the HTTP port) — use `local.issuer` when you need a
|
|
29
|
+
// different scheme/port, as below.
|
|
29
30
|
import { App, FrontMcp, Tool, ToolContext, z } from '@frontmcp/sdk';
|
|
30
31
|
|
|
31
32
|
@Tool({
|
|
@@ -65,7 +66,7 @@ class Server {}
|
|
|
65
66
|
|
|
66
67
|
- Relying on request-host-derived OAuth discovery, which works behind a tunnel or under an http.entryPath without extra config
|
|
67
68
|
- Setting `local.issuer` to a full public HTTPS URL so the token `iss` matches what clients reach through the proxy
|
|
68
|
-
- Knowing `
|
|
69
|
+
- Knowing the issuer order: `local.issuer`, then `FRONTMCP_PUBLIC_URL`, then `FRONTMCP_PUBLIC_HOST` (host only), then the request
|
|
69
70
|
|
|
70
71
|
## Related
|
|
71
72
|
|
|
@@ -39,6 +39,8 @@ auth: {
|
|
|
39
39
|
|
|
40
40
|
Tokens are compared in constant time over SHA-256 digests, so neither the value nor its length leaks by timing. A match yields a session whose `sub` is `static:<12 hex chars>` — a non-reversible digest prefix of the matching token, so audit logs can tell configured tokens apart without the secret appearing anywhere. Anything else, including a missing credential, is a `401` with a `WWW-Authenticate: Bearer realm="…"` challenge.
|
|
41
41
|
|
|
42
|
+
Public, anonymous-transparent and static callers get their claims from `anonymousCallerClaims(options, anonymousId)` in `@frontmcp/auth` (one shape whether the caller starts a session, resumes one, or sends a sessionless MCP 2026-07-28 request). Options: `issuer` (required, the `iss` claim), `scopes` (default `['anonymous']`, space-joined into `scope`), and `subject`. Without `subject` the result is `{ sub: 'anon:<anonymousId>', name: 'Anonymous' }`; with it (static mode passes `static:<12 hex chars>`) `sub` is that subject and `name` is `'Static token'`. Pass a unique `anonymousId` per caller (FrontMCP uses the session `uuid`, the same one when the session starts and on every resume, so an anonymous caller keeps one `sub` for the whole session; or a fresh UUID for a sessionless request) so anonymous callers never share a `sub`-keyed partition. Up to 1.8.4 a new anonymous session's first request got a different `sub` from the rest of the session.
|
|
43
|
+
|
|
42
44
|
**Use when:** one shared secret is the right granularity and standing up OAuth 2.1 is not. Rotate by deploying with both the old and new token in `tokens`, then dropping the old one.
|
|
43
45
|
|
|
44
46
|
**Do not use when:** you need per-user identity, revocation, or progressive auth — use `local` or `remote`.
|
|
@@ -94,7 +94,9 @@ Local mode runs a built-in OAuth 2.1 authorization server and signs its own JWT
|
|
|
94
94
|
class Server {}
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
- `local.issuer` -- the `iss`
|
|
97
|
+
- `local.issuer` -- the issuer named everywhere: discovery's `issuer` and `authorization_servers`, the RFC 9207 `iss` on every authorization response (errors included), and the tokens' `iss`. Without it: `FRONTMCP_PUBLIC_URL` (plus the entry path) when pinned, else the `FRONTMCP_PUBLIC_HOST` boot-time issuer (`http://<host>:<port>`), else the request's origin -- the same on the Node server and under `createFetchHandler()`.
|
|
98
|
+
- The protected resource metadata's `scopes_supported` is what the mode grants: `allowedScopes` (local/remote), `anonymousScopes` (public), `scopes` (static), `requiredScopes` then `scopes` (transparent), plus `authProviders` scopes outside local/remote mode.
|
|
99
|
+
- An MCP 2026-07-28 request has no session: anonymous and static-key callers get none minted, so `MCP_SESSION_SECRET` is needed only for session clients; `this.context.verifiedSessionId` is `undefined` there.
|
|
98
100
|
|
|
99
101
|
Token signing uses **HS256, a symmetric secret** read from the `JWT_SECRET` environment variable -- there is **no RSA/EC key pair** and no key store. Generate a stable secret (`JWT_SECRET=$(openssl rand -hex 32)`); if it is unset, FrontMCP falls back to a random per-process secret and all tokens are invalidated on restart.
|
|
100
102
|
|
|
@@ -70,6 +70,8 @@ info or running tool:
|
|
|
70
70
|
- A request waiting on its client (elicitation, `roots/list`) steps aside while it waits.
|
|
71
71
|
- Concurrent tool calls inside one request (`Promise.all`) are refused with
|
|
72
72
|
`AsyncContextOverlapError` once they overlap. Run them one after another.
|
|
73
|
+
- A workflow runs its ready steps one at a time, whatever its `maxConcurrency`, so each step runs
|
|
74
|
+
once instead of overlapping and being retried.
|
|
73
75
|
- A tool must not call its own server through a `DirectClient`/`DirectMcpServer` (it waits for its
|
|
74
76
|
own turn); use `this.scope` flows. A request that waits more than 10s for its turn logs why.
|
|
75
77
|
- Timers and un-awaited promises must not read request context.
|
|
@@ -34,6 +34,16 @@ leaves out actions the caller can never run. When the server configures
|
|
|
34
34
|
`authorities`, bundle rules are evaluated with the server's engine (its
|
|
35
35
|
`claimsMapping`, resolvers and custom evaluators).
|
|
36
36
|
|
|
37
|
+
A bundle skill's `SKILL.md` is listed in `skill://index.json` at its name
|
|
38
|
+
(`skill://Invoices/SKILL.md`, as SEP-2640 requires) and is also served at the
|
|
39
|
+
same URI with its id (`skill://invoices/SKILL.md`), the id the meta-tools and
|
|
40
|
+
`skills/list` report, with the same gating. A skill whose id is another
|
|
41
|
+
skill's name is refused, so an id names one skill: a bundle with one is not
|
|
42
|
+
applied and the previous bundle stays active. The previous bundle's skills do
|
|
43
|
+
not count, so a new skill can take the name of a skill the bundle drops or
|
|
44
|
+
renames (`registerSkillContent`'s `supersedes` option, which the bundle sync
|
|
45
|
+
fills in).
|
|
46
|
+
|
|
37
47
|
For the conceptual picture, see [Skills-Only Deployment](https://docs.agentfront.dev/frontmcp/features/skills-only-deployment).
|
|
38
48
|
For the production-ready decorator build, see [`deploy-to-cloudflare.md`](./deploy-to-cloudflare.md).
|
|
39
49
|
|
|
@@ -159,10 +159,10 @@ To keep bindings out of `process.env` entirely, add `nodejs_compat_do_not_popula
|
|
|
159
159
|
|
|
160
160
|
`NODE_ENV = "production"` in `[vars]` makes this a production deployment, where FrontMCP refuses its development fallbacks:
|
|
161
161
|
|
|
162
|
-
| Secret | Required when
|
|
163
|
-
| -------------------- |
|
|
164
|
-
| `MCP_SESSION_SECRET` |
|
|
165
|
-
| `JWT_SECRET` | `auth.mode` is `local` or `remote` (these mint tokens)
|
|
162
|
+
| Secret | Required when | Failure without it |
|
|
163
|
+
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
164
|
+
| `MCP_SESSION_SECRET` | in production, for session clients (Durable Object sessions, protocol before 2026-07-28) — `session:verify` encrypts their session IDs with it; 2026-07-28 requests need none | `500 {"error":"server_misconfigured","code":"SESSION_SECRET_REQUIRED"}` |
|
|
165
|
+
| `JWT_SECRET` | `auth.mode` is `local` or `remote` (these mint tokens) | the server refuses to start; requests answer `500 {"error":"server_misconfigured","code":"JWT_SECRET_REQUIRED"}` |
|
|
166
166
|
|
|
167
167
|
```bash
|
|
168
168
|
npx wrangler secret put MCP_SESSION_SECRET # openssl rand -hex 32
|
|
@@ -172,6 +172,13 @@ npx wrangler secret put MCP_SESSION_SECRET # openssl rand -hex 32
|
|
|
172
172
|
npx wrangler secret put JWT_SECRET # openssl rand -hex 32
|
|
173
173
|
```
|
|
174
174
|
|
|
175
|
+
**When the server fails to start.** The Worker builds the server on its first request. A failed build is answered, never thrown to the platform, and the error's message is not echoed:
|
|
176
|
+
|
|
177
|
+
- A configuration fault (missing or weak secret, a startup check, a config the schema refuses): `500 {"error":"server_misconfigured","code":"…"}` — codes `SESSION_SECRET_REQUIRED`, `JWT_SECRET_REQUIRED`, `JWT_SECRET_INVALID`, `UNENFORCED_METADATA`, `AUTH_CONFIGURATION_ERROR`, `CONFIG_INVALID`.
|
|
178
|
+
- Anything else (a remote that refused the connection, a package that failed to load): `503 {"error":"server_unavailable","code":"SERVER_START_FAILED"}` with `Retry-After`.
|
|
179
|
+
|
|
180
|
+
The failure is kept until a retry delay passes (1 s, doubling up to 60 s); the first request after it builds again. The cause is logged once per attempt (`wrangler tail`). Same for `createEdgeMcp()`, `createFetchHandler()` on an edge isolate, and each session Durable Object.
|
|
181
|
+
|
|
175
182
|
Because `[vars]` reach `process.env`, `npx wrangler dev` sees the same `NODE_ENV=production` the deployment does, so a missing secret fails locally rather than only after a successful deploy.
|
|
176
183
|
|
|
177
184
|
### Background tasks
|
|
@@ -266,7 +273,8 @@ class_name = "FrontMcpSession"
|
|
|
266
273
|
[[migrations]]
|
|
267
274
|
tag = "v1"
|
|
268
275
|
new_classes = ["FrontMcpSession"]
|
|
269
|
-
# MCP_SESSION_SECRET is required on production isolates
|
|
276
|
+
# MCP_SESSION_SECRET is required on production isolates that serve session clients
|
|
277
|
+
# (Durable Object sessions, protocol before 2026-07-28). Set it as a SECRET, not
|
|
270
278
|
# a var — `[vars]` is committed plaintext:
|
|
271
279
|
# npx wrangler secret put MCP_SESSION_SECRET # openssl rand -hex 32
|
|
272
280
|
```
|
|
@@ -122,7 +122,7 @@ before the first `elicit()`/`sample()`/`listRoots()` call.
|
|
|
122
122
|
and a 10-minute expiry — a tampered or replayed blob is discarded and the
|
|
123
123
|
exchange restarts.
|
|
124
124
|
|
|
125
|
-
The client MUST declare the matching capability, or the server answers `-32021
|
|
125
|
+
The client MUST declare the matching capability, or the server answers `-32021`. (An unversioned call the server only defaulted to 2026-07-28 comes from a client that never declared it; `elicit()` answers that one with `ElicitationNotSupportedError`, as for a legacy client without a session.)
|
|
126
126
|
|
|
127
127
|
```json
|
|
128
128
|
"io.modelcontextprotocol/clientCapabilities": { "elicitation": { "form": {} } }
|
|
@@ -127,6 +127,10 @@ llm: {
|
|
|
127
127
|
},
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
+
## Invocation and Hooks
|
|
131
|
+
|
|
132
|
+
Calling `invoke_<agent>` runs `tools:call-tool` for the agent's tool (the agent's `authorities`, `rateLimit`, `concurrency`, `timeout` and plugin fields apply there), then `agents:call-agent`, which runs the agent. Hooks on agent invocation run in that flow: `AgentCallHook` in a plugin, or `@AgentCallHook.Will(...)` / `.Did(...)` methods on the agent class. `this.context` and `CONTEXT`-scoped providers are available inside the agent, including a custom `execute()`.
|
|
133
|
+
|
|
130
134
|
## Custom execute() vs Default Agent Loop
|
|
131
135
|
|
|
132
136
|
By default, calling `execute()` runs the full agent loop: the LLM receives the input plus system instructions, decides which inner tools to call, processes results, and iterates until it produces a final answer.
|
|
@@ -170,6 +170,8 @@ class StrictWorkflowSkill extends SkillContext {}
|
|
|
170
170
|
| `'warn'` | Logs a warning for missing tools but continues. Use during development when tools may not all be available yet. |
|
|
171
171
|
| `'ignore'` | Silently ignores missing tools. Use for optional tool references or cross-server skills. |
|
|
172
172
|
|
|
173
|
+
When a caller loads the skill (`skills/load`, the `skills:load` flow, `GET /skills/{id}`, `/llm_full.txt`), a referenced tool that `availableWhen.surface` doesn't offer that caller (an agent-only tool, for an MCP client) is reported as missing, without its input schema, just as `tools/list` leaves it out. The same skill loaded by an agent lists it as available.
|
|
174
|
+
|
|
173
175
|
## Instruction Sources
|
|
174
176
|
|
|
175
177
|
Skills support three ways to provide instructions.
|
|
@@ -145,7 +145,7 @@ export default skill({
|
|
|
145
145
|
|
|
146
146
|
### URL Reference
|
|
147
147
|
|
|
148
|
-
Load instructions from a remote URL.
|
|
148
|
+
Load instructions from a remote URL.
|
|
149
149
|
|
|
150
150
|
```typescript
|
|
151
151
|
@Skill({
|
|
@@ -156,6 +156,8 @@ Load instructions from a remote URL. Fetched at build time when the skill is loa
|
|
|
156
156
|
class ApiStandardsSkill extends SkillContext {}
|
|
157
157
|
```
|
|
158
158
|
|
|
159
|
+
> **When file and URL instructions are read:** when the server starts, for every skill — the server indexes each skill for `skills/search` and checks its tools then, so a URL is fetched at every start whether or not a client reads the skill. A read that fails is logged (`Failed to load skill <name>: …`) and tried again the first time the skill is loaded; the content is kept once a read succeeds.
|
|
160
|
+
|
|
159
161
|
## SkillContext: loadInstructions() and build()
|
|
160
162
|
|
|
161
163
|
The `SkillContext` class resolves instructions regardless of the source type. When the framework serves a skill, it calls `build()` which internally calls `loadInstructions()`.
|
|
@@ -178,6 +178,7 @@ CodeCallPlugin.init({
|
|
|
178
178
|
- `includeTools` and `directCalls.filter` receive the same object, with the tool's `annotations` and declared `metadata` (`tool.metadata?.annotations` is the same object as `tool.annotations`). It is a deep read-only copy, so a filter cannot change what the next decision reads.
|
|
179
179
|
- Namespace bindings (`mail.send({...})` for a tool named `mail.send`) are AgentScript wrappers over `callTool()` inside the sandbox: they count toward `vm.maxSteps` and pass the rate limit and suspicious-sequence checks exactly like `callTool('mail.send', {...})`. A binding with no argument sends `{}`.
|
|
180
180
|
- `codecall:execute` results never include a `stack`, in any environment. In `runtime_error`, `syntax_error` and `tool_error` messages, stack frames are dropped and absolute paths (POSIX, Windows, UNC, `file:` URLs, quoted paths) become `[path]`; other URLs are kept.
|
|
181
|
+
- `illegal_access` messages name the script's own lines (`FORBIDDEN_LOOP (line 3): …`), whatever the enclave's transform printed; a line that is none of the script's is left out. `tool_error` results carry no `toolInput` (deprecated in the schema, never set).
|
|
181
182
|
|
|
182
183
|
### Power Features
|
|
183
184
|
|
|
@@ -128,16 +128,16 @@ OpenapiAdapter.init({
|
|
|
128
128
|
|
|
129
129
|
With no `authProviderMapper`, `securityResolver` or `staticAuth`, the adapter sends **no** credentials: operations that require auth fail with `Authentication required for tool '…'` and a `SECURITY WARNING` is logged at startup. The caller's MCP token (`ctx.authInfo.token`) is never forwarded implicitly — not by default, and not when an `authProviderMapper` function returns `undefined` — because passing it to another API is token passthrough, which the MCP specification forbids. `passthroughCallerToken: true` is the explicit opt-in, used only after every other credential source came up empty.
|
|
130
130
|
|
|
131
|
-
| Risk Level | Strategy
|
|
132
|
-
| ---------- |
|
|
133
|
-
| LOW | `authProviderMapper` or `securityResolver`
|
|
134
|
-
| MEDIUM | `staticAuth`, `additionalHeaders`, or none
|
|
135
|
-
| HIGH | `includeSecurityInInput
|
|
136
|
-
| HIGH | `passthroughCallerToken: true`
|
|
131
|
+
| Risk Level | Strategy | Description |
|
|
132
|
+
| ---------- | ----------------------------------------------------------------------------------- | ---------------------------------------------------- |
|
|
133
|
+
| LOW | `authProviderMapper` or `securityResolver` | Auth from user context, not exposed to clients |
|
|
134
|
+
| MEDIUM | `staticAuth`, `additionalHeaders`, or none | Static credentials, or no credentials at all |
|
|
135
|
+
| HIGH | `includeSecurityInInput` (`true` or a list of schemes), or `securitySchemesInInput` | Auth fields exposed to MCP clients (not recommended) |
|
|
136
|
+
| HIGH | `passthroughCallerToken: true` | The MCP client's own token is sent to the API |
|
|
137
137
|
|
|
138
138
|
`passthroughCallerToken: true` scores HIGH alongside an `authProviderMapper` too (the token is sent when no mapper function returns a credential); only a `securityResolver` or a non-empty `staticAuth` leaves it unused.
|
|
139
139
|
|
|
140
|
-
Resolution order: `securityResolver` → `authProviderMapper` → `staticAuth` (fills every credential no mapper function returned; a mapped value wins) → `passthroughCallerToken`. A security scheme with no `authProviderMapper` entry is refused at startup unless `staticAuth` covers it, `additionalHeaders` carries its credential, `headersMapper` may set it (a header or cookie scheme, checked on each request), or `passthroughCallerToken` does for an HTTP bearer scheme (it sends the caller's token for it and logs a `SECURITY WARNING`; the caller's token never fills an API key, basic, OAuth2 or OpenID Connect scheme). An operation that requires auth is sent only with a credential for one of its own schemes, from a credential option, the tool input (`securitySchemesInInput`, `includeSecurityInInput`), `additionalHeaders` or `headersMapper`; otherwise it fails with `Authentication required for tool '…'`. A tool-input credential is used for a scheme only when no other source supplies one (a server credential always wins).
|
|
140
|
+
Resolution order: `securityResolver` → `authProviderMapper` → `staticAuth` (fills every credential no mapper function returned; a mapped value wins) → `passthroughCallerToken`. A security scheme with no `authProviderMapper` entry is refused at startup unless `staticAuth` covers it, `additionalHeaders` carries its credential, `headersMapper` may set it (a header or cookie scheme, checked on each request), or `passthroughCallerToken` does for an HTTP bearer scheme (it sends the caller's token for it and logs a `SECURITY WARNING`; the caller's token never fills an API key, basic, OAuth2 or OpenID Connect scheme). An operation that requires auth is sent only with a credential for one of its own schemes, from a credential option, the tool input (`securitySchemesInInput`, or `includeSecurityInInput`: `true` for every scheme, a list for the schemes it names, like `securitySchemesInInput`; the schemes a list leaves out still need a credential source), `additionalHeaders` or `headersMapper`; otherwise it fails with `Authentication required for tool '…'`. A tool-input credential is used for a scheme only when no other source supplies one (a server credential always wins).
|
|
141
141
|
|
|
142
142
|
## Spec Polling
|
|
143
143
|
|
|
@@ -573,7 +573,7 @@
|
|
|
573
573
|
"features": [
|
|
574
574
|
"Relying on request-host-derived OAuth discovery, which works behind a tunnel or under an http.entryPath without extra config",
|
|
575
575
|
"Setting `local.issuer` to a full public HTTPS URL so the token `iss` matches what clients reach through the proxy",
|
|
576
|
-
"Knowing `
|
|
576
|
+
"Knowing the issuer order: `local.issuer`, then `FRONTMCP_PUBLIC_URL`, then `FRONTMCP_PUBLIC_HOST` (host only), then the request"
|
|
577
577
|
]
|
|
578
578
|
},
|
|
579
579
|
{
|