Skip to content
Automation
Command

/bootstrap

Build the bootstrap endpoint — per-user MCP servers, skills, dynamic config

From plugin
financial-services
34k56 skills10 agents56 commands2 MCP
Install
> /plugin marketplace add anthropics/financial-services

How it fires

How this command gets triggered: by you, by Claude, or both.

  • Fires itselfClaude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/bootstrap

Context preview

What this command does when you run it.

Build the bootstrap endpoint — per-user MCP servers, skills, dynamic config

Command definition

bootstrap.md
description: Build the bootstrap endpoint — per-user MCP servers, skills, dynamic config

Bootstrap endpoint

You host an HTTPS GET handler. The add-in calls it at startup with the user's Entra token, you return per-user JSON, the response overrides manifest and extension attrs for that user. This is how you push structured config — `mcp_servers`, `skills` — that flat string attrs can't carry.

Ask first

Figure out which mode you're in before walking the spec:

  • **Just want to understand it?** Answer from the sections below. Common

questions: what's the response shape, how does `{{...}}` work, why is CORS biting me.

  • **Building one?** Ask: new handler or editing an existing one? Lambda,

Cloud Function, Express, Python, something else? Then jump to [Scaffolding](#scaffolding-a-handler) — the sections in between are the contract you're coding against.

This vs extension attrs

Both deliver per-user config. Pick by what you're carrying.

| | [Extension attrs](update-user-attrs.md) | Bootstrap endpoint | |---|---|---| | You write | `az rest PATCH` per user | An HTTPS service | | Carries | Flat strings, ≤256 chars | Any JSON — arrays, nested, base64 | | Good for | Token rotation, region override | `mcp_servers`, `skills`, anything structured | | Refresh | Token cache, ~1hr lag | `bootstrap_expires_at`, you control it | | Auth | Entra token claims (passive) | You validate the JWT (active) |

If you only need to swap `gateway_token` per user, attrs are less work. The moment you want a Linear MCP server for one team and a Jira one for another, you're here.

Template interpolation

Any string value can contain `{{key}}`. The add-in substitutes against the **merged config chain** — manifest params, then extension attrs, then this response, each layer overriding the last. You don't echo a value back just so a template can see it; if `gateway_token` is already in the manifest or an attr, `{{gateway_token}}` resolves.

Two phases, because the request has to happen before the response exists:

1. **`bootstrap_url` itself** resolves against manifest + attrs only. So the manifest can carry `bootstrap_url=https://config.internal/bootstrap?project={{gcp_project_id}}` and you run one endpoint that branches on a query param instead of stamping per-team URLs into attrs. 2. **Response fields** resolve against the full merge — manifest + attrs + whatever this response just returned. An `mcp_servers` entry can reference a `gateway_token` that lives three lines up in the same JSON.

Unresolved `{{key}}` (typo, key never set anywhere) is left as-is in the string — no error, no empty-substitution. If an MCP server isn't connecting, check the URL the add-in actually constructed.

CORS — every URL needs it

The add-in is a browser. Every fetch — `bootstrap_url`, every `mcp_servers[].url`, every `skills[].url` — happens browser-side from inside the Office taskpane. Without `Access-Control-Allow-Origin: https://pivot.claude.ai` on the response, the browser blocks it before the add-in sees a byte. The server returns 200, the add-in gets nothing, and nothing in the add-in's logs tells you why. This is the most common "it's not working" cause.

| URL | Where CORS lives | |---|---| | `bootstrap_url` | Your handler's response headers. Behind API Gateway / Cloud Functions, also configure the `OPTIONS` preflight — the browser sends one before any request with custom headers. See the recommended preflight response below. | | `mcp_servers[].url` | The MCP server itself. Public ones (Linear, Atlassian) already allow it. Internal ones almost certainly don't until you add it. | | `skills[].url` | **The bucket, not the URL.** Presigned URLs auth the request — they don't grant CORS. S3 needs a bucket CORS config, GCS needs `gsutil cors set`, Azure needs blob service CORS rules. | | `otlp_endpoint` | Your OTEL collector's HTTP receiver. Most collectors default to same-origin only — set `cors.allowed_origins` on the OTLP/HTTP receiver. |

For `bootstrap_url`, the recommended preflight response is:

Access-Control-Allow-Origin:  https://pivot.claude.ai
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: Authorization, X-Claude-User-Agent, *

Allowing `*` for request headers is safe here — security comes from the Entra token, not header filtering — and keeps preflights working if the add-in adds headers in future. Keep `Allow-Origin` pinned to `https://pivot.claude.ai`.

The presigned-URL one bites hardest because `curl` works (curl ignores CORS), the signature is valid, the object exists, and the skill still doesn't load. Set bucket CORS once:

// S3 — aws s3api put-bucket-cors --bucket <name> --cors-configuration file://cors.json
{ "CORSRules": [{ "AllowedOrigins": ["https://pivot.claude.ai"], "AllowedMethods": ["GET"], "AllowedHeaders": ["*"] }] }
# GCS — gsutil cors set cors.json gs://<bucket>
[{"origin": ["https://pivot.claude.ai"], "method": ["GET"], "responseHeader": ["*"]}]
# Azure — az storage cors add --services b --methods GET --origins https://pivot.claude.ai --allowed-headers '*' --account-name <name>

If you're debugging a CORS failure: open the browser devtools inside the taskpane (right-click → Inspect on Windows, or attach via Safari's Develop menu on Mac), look for the request in the Network tab. A CORS block shows as a failed request with no response body and a console error naming the origin.

Request

GET <bootstrap_url>                            # after interpolation
Authorization: Bearer <entra_token>            # only if entra_sso=1 in manifest
X-Claude-User-Agent: claude-<app>/<version>    # always sent

`X-Claude-User-Agent` identifies which Office host the add-in is running in. `<app>` is one of `word`, `excel`, or `powerpoint`; `<version>` is the add-in build (e.g. `claude-excel/1.4.2`). Use it to return different skills or MCP servers per Office product, or to gate the add-in to specifi

Read more
Ships withfinancial-services

Reference agents, skills, and data connectors for the financial-services workflows we see most — investment banking, equity research, private equity, and wealth management.

Get the whole plugin