diff --git a/docs/webmcp.js b/docs/webmcp.js new file mode 100644 index 00000000..8a18969a --- /dev/null +++ b/docs/webmcp.js @@ -0,0 +1,62 @@ +/** + * WebMCP tool for agents running in the visitor's browser + * (https://webmachinelearning.github.io/webmcp/). + * + * Mintlify already registers search_docs, read_page, navigate_page, + * get_site_overview, and read_skill on every docs page. This adds the one + * thing those don't cover: how to connect to the Terminal49 MCP server. + * The tool name must not collide with Mintlify's, or registration fails. + * + * Mintlify loads every .js file in docs/ on every page, so this is plain + * browser JavaScript with no build step. + */ +(function () { + var MCP_URL = 'https://mcp.terminal49.com'; + + // Older Chrome builds expose the API on navigator instead of document. + var modelContext = document.modelContext || navigator.modelContext; + if (!modelContext || typeof modelContext.registerTool !== 'function') return; + + // Top-level page only, once: Mintlify navigates client-side and may run + // custom scripts again, and embedded frames would register a duplicate. + if (window.top !== window || window.__t49WebMcpRegistered) return; + window.__t49WebMcpRegistered = true; + + var tool = { + name: 'get_terminal49_mcp_connection', + title: 'Get Terminal49 MCP server connection details', + description: + 'Get the URL, transport, and sign-in details for the Terminal49 MCP server, plus setup guides for Claude, ChatGPT, Cursor, VS Code, and other MCP clients. Use this to connect an agent to a Terminal49 account to search, track, and monitor shipments.', + inputSchema: { type: 'object', properties: {} }, + annotations: { readOnlyHint: true }, + execute: function () { + var docs = window.location.origin + '/docs/mcp'; + return Promise.resolve({ + url: MCP_URL, + transport: 'streamable-http', + authentication: + 'OAuth 2.1 through auth.terminal49.com, discovered from the protected resource metadata; or send a Terminal49 API key as "Authorization: Token YOUR_API_KEY".', + protected_resource_metadata: MCP_URL + '/.well-known/oauth-protected-resource', + docs: docs + '/home', + setup_guides: { + claude: docs + '/setup/claude', + claude_code: docs + '/setup/claude-code', + chatgpt: docs + '/setup/chatgpt', + cursor: docs + '/setup/cursor', + vs_code: docs + '/setup/vs-code', + microsoft_copilot: docs + '/setup/microsoft-copilot', + other_clients: docs + '/setup/other-clients', + }, + }); + }, + }; + + // The executor catches a synchronous throw (e.g. InvalidStateError) as well + // as a rejection, so a failed attempt always clears the flag for a retry. + new Promise(function (resolve) { + resolve(modelContext.registerTool(tool)); + }).catch(function (error) { + window.__t49WebMcpRegistered = false; + if (window.console && console.warn) console.warn('WebMCP: could not register tool', error); + }); +})(); diff --git a/packages/mcp/WORKOS_MCP_SETUP.md b/packages/mcp/WORKOS_MCP_SETUP.md index d3cae7f2..d2339d0a 100644 --- a/packages/mcp/WORKOS_MCP_SETUP.md +++ b/packages/mcp/WORKOS_MCP_SETUP.md @@ -53,7 +53,7 @@ In the WorkOS environment referenced by `WORKOS_AUTHORIZATION_SERVER_URL`: | `T49_MCP_AUTHKIT_ENABLED` | `true` | Otherwise a WorkOS `Bearer` token is treated as a passthrough API key and fails | | `T49_CONNECTED_CLIENTS_RESOLVE_SECRET` | the resolve shared secret | Required to call `/connected-clients/resolve` | | `T49_MCP_ALLOWED_HOSTS` | include `mcp.terminal49.com` (if set at all) | Host allowlist; missing host → 403 | -| `T49_MCP_SCOPES_SUPPORTED` | **leave unset** | WorkOS only issues `openid/profile/email/offline_access`; advertising `mcp:tools` etc. causes `invalid_scope` | +| `T49_MCP_SCOPES_SUPPORTED` | `openid,profile,email,offline_access` | Advertised as `scopes_supported` in the PRM document. Must match what WorkOS issues (the AS metadata's `scopes_supported`); advertising scopes it doesn't issue, such as `mcp:tools`, causes `invalid_scope` | | `POSTHOG_PROJECT_API_KEY` | the PostHog project API key | Enables MCP tool-usage analytics. **Leave unset and the integration is inert** — no client, no network call | | `POSTHOG_HOST` | optional; defaults to `https://f.terminal49.com` | First-party ingestion proxy, same host the docs site uses (`docs/docs.json`) | | `POSTHOG_ENABLED` | optional; defaults to `true` | Set `false` to kill-switch analytics without removing the key | @@ -83,8 +83,9 @@ curl --fail --silent --show-error -X POST "$ISSUER/oauth2/register" \ -d '{"client_name":"smoke","redirect_uris":["https://example.com/cb"],"grant_types":["authorization_code","refresh_token"],"response_types":["code"],"token_endpoint_auth_method":"none"}' \ | jq --exit-status '.client_id != null' > /dev/null -# PRM: resource + authorization_servers -curl -s https://mcp.terminal49.com/.well-known/oauth-protected-resource | jq +# PRM: resource, authorization_servers, and the WorkOS scopes +curl -s https://mcp.terminal49.com/.well-known/oauth-protected-resource \ + | jq --exit-status '.scopes_supported == ["openid","profile","email","offline_access"]' # 401 challenge points at the PRM (must be POST — a GET to /mcp returns 405) curl -si -X POST https://mcp.terminal49.com/mcp \