Skip to content
Merged
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
22 changes: 22 additions & 0 deletions doctrine/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,28 @@ evidence (Run Records / Retro Notes) behind it. Required by constitution
invariant I8; written by `/tune-pipeline` when a human approves a
proposal, or by hand for direct human edits.

## 2026-09-17 — align in-app setup steps with current Control Plane UI

Files: `doctrine/speakeasy-setup.md`, `guides/*/speakeasy.md`,
`go/generated/**`.

Evidence: the human reported that **Configure Manually** was absent in
their setup experience and approved the concrete correction scope and
copy in this conversation. Source audit against `speakeasy-api/gram`
main `8fa18729608e34de305e789b53f36eb2c6c853c9` found state-dependent
authentication controls and stale navigation/creation instructions.

- Use MCP Gateway → MCP → Add new; distinguish catalog installation's
Configure MCP settings action from custom-remote Verify connectivity → Save.
- Handle configured authentication, existing providers/clients, and new
issuer setup explicitly; do not globally replace Configure Manually.
- Treat discovery as automatic when seeded; check the redirect URI before
attachment closes the sheet. Preserve provider-specific credentials.
- Refresh the authored guides and regenerate their embedded copies.

Verification is source-level plus repository validation; this entry does
not claim a production-browser walkthrough. I8: human-approved correction.

## Bounded decision evidence file transport

Files: `factory/prompts/{endpoint,reconcile,finalize-research}.md`.
Expand Down
189 changes: 107 additions & 82 deletions doctrine/speakeasy-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,19 @@ Consumers may omit this file when Speakeasy setup is already in context
`external.md`).

UI facts below are drawn from the product source
(`speakeasy-api/gram`, `client/dashboard`, branch `main`): add-server and
Manual OAuth / Upstream Headers labels from commit `96f7f73` (observed
2026-07-23); Dynamic Client Registration (DCR) attach-sheet labels from
commit `f1d60da` (observed 2026-07-27). Labels are verbatim code-level
strings; a rendered-UI spot check on first use is still worthwhile. No
role may invent a label this file does not carry.
(`speakeasy-api/gram`, `client/dashboard`, branch `main`), commit
`8fa18729608e34de305e789b53f36eb2c6c853c9` (observed 2026-09-17).
Navigation and creation: `pages/mcp/MCP.tsx`, `pages/mcp/add/AddMcpServer.tsx`,
`pages/sources/remote-mcp/CreateRemoteMcp.tsx`, and `pages/catalog/`.
Authentication: `pages/mcp/x/tabs/settings/sections/authentication/`.
Labels are code-level strings; a rendered-UI spot check is still worthwhile.
Controls depend on configuration state and write permission. No role may
invent a label this file does not carry.

## Per-guide values (recorded in the Dossier's Speakeasy setup section)

- `<remote URL>` — from `meta.yaml` `remotes`. (The Control Plane
proxies remote servers over streamable-http; the add form's
**Transport** field is read-only.) Mark each remote
- `<remote URL>` — from `meta.yaml` `remotes`. The Control Plane
proxies remote servers over streamable-http. Mark each remote
`tenanted: true` when the reader must paste a region, instance, or
org-specific URL rather than a single shared public endpoint. When the
URL is shared but the guide must still skip the catalog (unreliable
Expand All @@ -34,9 +35,12 @@ role may invent a label this file does not carry.
`custom-remote`.
- The Authentication Option the guide documents, which External-setup
step produced each credential field, and — for OAuth options — any
scopes the provider requires. For DCR, also record the **Issuer URL**
(often the remote origin) when the Control Plane cannot discover it
from protected-resource metadata alone.
scopes the provider requires. For every OAuth option, record the
**Issuer URL**, discovery support, and documented authorization/token
endpoints when discovery is unavailable; DCR also needs a registration
endpoint. Do not assume the remote MCP URL is the OAuth issuer. Missing
provider-specific issuer/endpoint evidence is an open question, not a
value the Writer may invent.
- `<further-reading URL>` — the provider's primary MCP documentation
page, for the closing pointer.

Expand All @@ -52,7 +56,7 @@ override applies.
- `custom-remote` → Custom remote only
- `catalog` → catalog only
- `auto` / omitted → Pulse catalog presence in operator notes:
- **present** → catalog (3rd-party server) only
- **present** → catalog (**From the catalog**) only
- **absent** → Custom remote only
- **ambiguous** / **skipped** / no lookup → both bullets + soft
catalog-presence open question
Expand All @@ -70,91 +74,112 @@ when presence is known.

### Add the server in Speakeasy {#add-server-in-speakeasy}

In the Speakeasy AI Control Plane sidebar, under **Connect**, select
**Sources**, then click **Add Source**.
In the Speakeasy AI Control Plane sidebar, under **MCP Gateway**, select
**MCP**, then click **Add new** to open **Add MCP server**.

**Catalog path** (Pulse **present** with `auto`, or
`speakeasy_add_server: catalog`; never when tenanted or
`custom-remote`): choose **3rd-party server**. On the **MCP Catalog**
page, find <Provider> (the search box reads **Search MCP servers...**),
open its entry with **View**, and click **Add**. In the **Add to
Project** dialog, click **Add to Project**.
`custom-remote`): choose **From the catalog**. On the **MCP Catalog**
page, find <Provider> using **Search MCP servers...**, open its catalog
entry, and click **Add**. In the **Add to Project** dialog, click
**Add to Project**. Wait for installation to complete, then click
**Configure MCP settings** to open the created server.

**Custom remote path** (tenanted, `speakeasy_add_server: custom-remote`,
or Pulse **absent**): choose **Custom remote server**. On the **Add a
custom remote MCP server** page, paste `<remote URL>` into **Remote MCP
server URL** and click **Add server**.
or Pulse **absent**): choose **Hosted remotely**. On **New remote MCP
server**, paste `<remote URL>` into **MCP server URL**. Optionally enter
**Display name (optional)**. Click **Verify connectivity**, then, after
verification succeeds, click **Save**. This creates the hosted MCP server
and opens its **Overview** page.

**Dual conditional** (Pulse **ambiguous** / **skipped** only, `auto`,
and not tenanted / not forced) — keep both as bullets:

- If <Provider> is in the catalog: choose **3rd-party server**. On the
**MCP Catalog** page, find <Provider> (the search box reads
**Search MCP servers...**), open its entry with **View**, and click
**Add**. In the **Add to Project** dialog, click **Add to Project**.
- If it is not: choose **Custom remote server**. On the
**Add a custom remote MCP server** page, paste `<remote URL>` into
**Remote MCP server URL** and click **Add server**.
- If <Provider> is in the catalog: choose **From the catalog**. Find
<Provider> using **Search MCP servers...**, open its catalog entry,
and click **Add**. In **Add to Project**, click **Add to Project**.
After installation, click **Configure MCP settings**.
- If it is not: choose **Hosted remotely**. On **New remote MCP server**,
paste `<remote URL>` into **MCP server URL**. Click **Verify
connectivity**, then **Save** after verification succeeds. This opens
the server's **Overview** page.

Either resolved path (or either dual branch) creates the hosted MCP
server and opens its **Overview** page. When only one path is emitted,
close with: This creates the hosted MCP server and opens its
**Overview** page.
Do not describe catalog installation as automatically opening Overview.

<!-- screenshot: the Add Source menu open on the Sources page, or the provider's catalog entry -->
<!-- screenshot: Add MCP server choices, or the provider's catalog entry -->

### Connect your credentials {#connect-speakeasy-credentials}

From the server's **Overview**, open **Settings**. The Writer renders
only the variant matching the guide's Authentication Option, names the
guide's actual fields, and cross-links each value to the External-setup
step that produced it (or, for DCR with no External credentials, to the
step that produced the issuer / region URL).

- OAuth with a pre-registered client: under **Authentication**, click
**Configure Manually** (or **Use Discovered** when offered — the
Dossier records whether the provider publishes discoverable OAuth
metadata). In the **Attach Remote Identity Provider** sheet, set
**Client Type** to **Manual**. The sheet shows the **Redirect URI**
with a copy button — the callback URL the guide had the reader
register in External setup (`{{ gram.oauth.callback_url }}`).
<!-- verify(operator): the template key substitutes this same Redirect URI value -->
Paste the **Client ID** and **Client Secret (optional)** from
External setup, then click **Attach Identity Provider**. Confirm the
sheet's **Redirect URI** matches the `{{ gram.oauth.callback_url }}`
value registered under the provider's redirect/callback field in
External setup — readers paste that template key directly there; they
do not visit this sheet mid–External-setup only to copy the URI.
- OAuth with Dynamic Client Registration (DCR): under **Authentication**,
click **Configure Manually** (or **Use Discovered** when offered — the
Dossier records whether protected-resource metadata makes discovery
available without a pasted issuer). In the **Attach Remote Identity
Provider** sheet, when the issuer is not already known, paste the
provider **Issuer URL** from External setup (typically the remote MCP
origin). Keep the auto-derived **Slug** and **Display name (optional)**
unless the Dossier records a project naming requirement. Under
**Endpoints**, click **Discover** so authorization, token, and
registration endpoints fill from the provider's authorization-server
metadata. Under **Session Client**, keep **Client Type** set to
**Dynamic Client Registration (DCR)** (the default when a registration
endpoint is discovered). Keep **Token Endpoint Auth Method** at the
Open the server's **Settings**. The Writer renders only the variant
matching the guide's Authentication Option, names the guide's actual
fields, and cross-links each value to the External-setup step that
produced it. Include provider-specific issuer and endpoint values from
the Dossier where needed, rather than making readers guess.

For OAuth, under **Authentication**:

- If authentication is not configured, choose **Use Discovered** when
available; otherwise choose **Configure Manually**.
- If authentication is already configured, find **Connected services**.
If no provider is attached, click **Add provider**. If the intended
provider is already attached, review its existing configuration instead
of attaching it again. Adding another provider is not available for
every server type. Changes require write permission.

In **Attach Remote Identity Provider**, choose **Select existing** to
reuse the intended project provider, or **Add new** to configure one.
The selector appears when existing providers are available; otherwise
the new-provider form is shown directly. For a new provider, enter the
provider's **Issuer URL** when not already populated, retain the derived
**Slug**, and optionally set **Display name (optional)**. A discovered
issuer starts endpoint discovery automatically. Verify the populated
endpoints; if entering or changing the issuer manually, click **Discover**
under **Endpoints** when available. If discovery is unavailable, use the
documented authorization and token endpoints (and registration endpoint
for DCR).

Under **Session Client**, reuse the intended existing client with
**Select existing**, or choose **Add new** when that selector is shown.
Reusing a client uses its stored credentials, scopes, and audience; do
not instruct readers to re-enter new-client fields in this branch.
Confirm its read-only configuration matches the guide. If it does not,
choose **Add new** rather than implying the attach sheet can edit a reused
client. For a pre-registered client, check the provider's registered
callback against `{{ gram.oauth.callback_url }}` before attachment;
**Redirect URI** is not displayed when selecting an existing client. Then click **Attach Identity Provider**. The
following credential variants apply to a **new** session client:

- OAuth with a pre-registered client: set **Client Type** to **Manual**.
Paste the **Client ID** and **Client Secret (optional)** from External
setup, and any provider-required overrides. **Scope (override)** takes
comma-separated scopes. The label does not make a
secret optional when the provider requires it. Before clicking
**Attach Identity Provider**, confirm the displayed **Redirect URI**
matches the callback URL registered during External setup
(`{{ gram.oauth.callback_url }}`). Readers receive the rendered callback
URL, not the literal template key. Successful attachment closes the
sheet, so do not put this check after attachment.
- OAuth with Dynamic Client Registration (DCR): verify the registration
endpoint is populated, then set **Client Type** to **Dynamic Client
Registration (DCR)**. Keep **Token Endpoint Auth Method** at the
discovered default unless the Dossier records a required override.
Leave **Scope (override)** and **Audience (optional)** empty unless
the Dossier records values to enter. Click **Attach Identity
Provider**. The Control Plane registers the OAuth client at the
provider's registration endpoint — there is no **Client ID** or
**Client Secret** to paste, and readers do not register
`{{ gram.oauth.callback_url }}` on the provider for this path. When a
client first needs provider access, complete the provider's on-screen
browser authorization prompts with the intended account (exact prompt
labels are provider-specific; do not invent them).
- API key / token: under **Upstream Headers**, click **Add header**,
enter the **Header name** (for example `Authorization`), leave
**Value source** as **Static value**, paste the value from External
setup, check **Secret**, and click **Save**. (Catalog installs may
collect the same headers earlier, in the **Add to Project** dialog's
**Upstream headers** section.)
<!-- screenshot: the Attach Remote Identity Provider sheet (Manual with Redirect URI, or DCR after Discover with Client Type Dynamic Client Registration), or the Upstream Headers editor; values redacted -->
the Dossier records values to enter. Click **Attach Identity Provider**.
The Control Plane registers the OAuth client at the provider's
registration endpoint — there is no **Client ID** or **Client Secret**
to paste, and readers do not register `{{ gram.oauth.callback_url }}`
on the provider for this path. When a client first needs provider
access, complete the provider's browser authorization prompts with the
intended account (exact prompt labels are provider-specific).

For an API key / token, under **Upstream Headers**, click **Add header**,
enter the **Header name** (for example `Authorization`), leave **Value
source** as **Static value**, paste the value from External setup, check
**Secret**, and click **Save**. Catalog installs may collect these headers
earlier in **Add to Project** under **Upstream headers**; do not add them
a second time.

<!-- screenshot: Attach Remote Identity Provider with new/existing selection and Manual or discovered DCR fields, or Upstream Headers; values redacted -->

## The closing pointer

Expand Down
59 changes: 46 additions & 13 deletions guides/asana/speakeasy.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,62 @@

### Add the server in Speakeasy {#add-server-in-speakeasy}

1. In the Speakeasy AI Control Plane sidebar, under **Connect**, select **Sources**.
2. Select **Add Source**.
3. Choose **3rd-party server**.
1. In the Speakeasy AI Control Plane sidebar, under **MCP Gateway**, select **MCP**.
2. Select **Add new** to open the **Add MCP server** page.
3. Choose **From the catalog**.
4. On the **MCP Catalog** page, find **Asana** using **Search MCP servers...**.
5. Select **View**.
5. Open the **Asana** entry.
6. Select **Add**.
7. In **Add to Project**, select **Add to Project**.

This creates the hosted MCP server and opens its **Overview** page.
After installation, select **Configure MCP settings** on the completion screen to open the server, then open **Settings**.

<!-- screenshot: the Add Source menu on Sources, or Asana's catalog entry -->
<!-- screenshot: the Add MCP server page, or Asana's catalog entry -->

### Connect your credentials {#connect-speakeasy-credentials}

Select **Configure MCP settings** on the completion screen, then open the server’s **Settings**.

Under **Authentication**, if unconfigured, select **Use Discovered** when available; otherwise select **Configure Manually**. If configured but no provider is attached, use **Connected services > Add provider**. If the intended provider is already attached, use its existing controls and skip the provider/client creation and attachment steps below; do not add a duplicate.

#### Select the identity provider

In **Attach Remote Identity Provider**, **Identity Provider** defaults to **Select existing** when project issuers are available. Select the matching provider and skip the new-provider fields below. Otherwise choose **Add new** (or use the new-provider form shown when none exist).

For a new provider only, confirm **Issuer URL**, the auto-derived **Slug**, and **Endpoints**. Discovery runs automatically for a seeded issuer; after typing or changing the URL, select **Discover** only if offered.

For a new provider, enter **Issuer URL** `https://app.asana.com` and keep the auto-derived **Slug**. If discovery does not populate **Endpoints**, enter:

Authorization endpoint:

```text
https://app.asana.com/-/oauth_authorize
```

Token endpoint:

```text
https://app.asana.com/-/oauth_token
```

<!-- source: https://app.asana.com/.well-known/oauth-authorization-server; public metadata checked 2026-09-17 -->

#### Select the session client

Under **Session Client**, choose **Select existing** only for a client whose saved credentials, scopes, and audience match the requirements below; otherwise choose **Add new**. When reusing a matching client, skip directly to **Verify the callback and attach** below. Do not create credentials or register the client again. Otherwise choose **Add new** (or use the new-client form shown when no clients exist) and complete these new-client-only steps:

Do not enter a scope during this setup.

1. From the server's **Overview**, open **Settings**.
2. Under **Authentication**, select **Use Discovered** when offered; otherwise, select **Configure Manually**.
3. In **Attach Remote Identity Provider**, set **Client Type** to **Manual**.
4. Confirm that **Redirect URI** matches the `{{ gram.oauth.callback_url }}` value entered in [Configure the OAuth redirect](external.md#configure-oauth-redirect).
5. Paste the **Client ID** saved in [Create the MCP app](external.md#create-mcp-app) into **Client ID**.
6. Paste the **Client secret** saved in [Create the MCP app](external.md#create-mcp-app) into **Client Secret (optional)**.
7. Select **Attach Identity Provider**.
1. In **Attach Remote Identity Provider**, set **Client Type** to **Manual**.
1. Paste the **Client ID** saved in [Create the MCP app](external.md#create-mcp-app) into **Client ID**.
1. Paste the **Client secret** saved in [Create the MCP app](external.md#create-mcp-app) into **Client Secret (optional)**.

#### Verify the callback and attach

1. Confirm that the callback URL registered with the provider is `{{ gram.oauth.callback_url }}`. For a new manual client, also compare it with the sheet's displayed **Redirect URI**. The existing-client selection does not display that field; check the registered callback in the provider's app settings instead.
2. Click **Attach Identity Provider**.

For the provider-side callback setting, see [Configure the OAuth redirect](external.md#configure-oauth-redirect).

<!-- screenshot: Attach Remote Identity Provider with the Redirect URI and credential fields visible and all credential values redacted -->

Expand Down
Loading
Loading