Connected resources
A connected resource is a normal remote MCP service that accepts the identity the user already selected in WAMP. This page is sufficient to build and deploy the backend, register it, create the customer consent link, and package the Desktop connector without access to the WAMP source repository. The user does not create an account in your service, run another OAuth flow, or copy an API key.
You need a WAMP account with organization.applications.manage in the
organization that will publish the service. If you do not see Apps in
Account Center, ask an organization owner to
grant that permission or perform the registration steps.
Production values
Section titled “Production values”These values are fixed for the public WAMP deployment. Copy them as written:
| Variable | Value |
|---|---|
WAMP_ACCOUNT_API |
https://api.vampikez.fun |
WAMP_TOKEN_ISSUER |
https://api.vampikez.fun |
WAMP_JWKS_URL |
https://api.vampikez.fun/.well-known/jwks.json |
| Account Center | https://account.vampikez.fun |
For another WAMP deployment, its operator supplies these values. The Account
API and token issuer are intentionally distinct from the OIDC issuer ending in
/oauth2; do not use the OIDC issuer or its JWKS URI for resource identity
tokens.
What is available now
Section titled “What is available now”| Path | Status |
|---|---|
| Backend authentication and standard Streamable HTTP MCP | Available |
| Manifest-only connector imported into WAMP Desktop | Available |
| Native and ACP agents in Desktop | Available; the ACP runtime must lend the mcp tool category |
| Native and ACP agents in managed Cloud | Runtime and identity path available when the connector is included in the Cloud engine image |
| User-installed Marketplace connector automatically appearing in Cloud | Not available; arbitrary extensions are not yet delivered into a managed sandbox |
| Extension UI reading your HTTP API as the viewer | Available in WAMP Desktop through auth.resource and pluginAPI.auth.resourceFetch; not yet in managed Cloud or the web workspace |
The backend contract does not change across those paths. A backend built from this page is Cloud-compatible; until Cloud extension delivery ships, a WAMP operator must include the same manifest in the Cloud image. Do not put a token in renderer code as a workaround.
How a request reaches your service
Section titled “How a request reaches your service”- An organization authorizes the exact registered resource audience and enables its App for all members or an assigned subset.
- An installed extension contributes your standard Streamable HTTP MCP URL and registered resource audience.
- WAMP reauthorizes the current Desktop identity or the Cloud Session’s durable execution owner, then mints a short-lived JWT for that exact audience and network origin.
- The engine sends the JWT as an ordinary bearer on every MCP HTTP request.
- Your service verifies the JWT locally and applies its own tenant and resource
ACL using
org_idand the pairwisesub. - Native WAMP agents use the discovered tools directly. ACP agents receive the same tools through WAMP’s MCP lending boundary; the JWT and upstream connection never enter the ACP process.
There is no WAMP-specific MCP dialect and no WAMP SDK dependency in your
service. Authentication is the only added HTTP boundary. A connector does not
need an engine or ACP patch; an ACP runtime only needs its existing tool
selection to lend the mcp category.
Register the resource
Section titled “Register the resource”Use Account Center for the shortest path:
- Select your publisher organization, open Apps, and choose New app.
Set a stable lowercase slug such as
internal-knowledge. - Open the App, choose Add key, then Generate. Download the private key when shown; WAMP stores only the public half and cannot recover the private key. Confirm that you saved it to register the public key.
- Under Connected resources, choose New resource and enter the name, audience, canonical HTTPS origin, and namespace described below.
- Copy the App ID shown above the resource list. Your backend verifies it
as
WAMP_RESOURCE_APP_ID.
No OAuth client or WAMP capability definition is required. The signing key is used only by your backend to create installation consent links; it is never sent to the MCP endpoint or included in the extension.
The same registration is available through the public Account API for automation. Its exact request and user-token contract are in Apps and credentials; the Account Center path above requires no token handling.
| Value | Contract |
|---|---|
| App slug | The existing WAMP App that owns the service. It does not have to equal the extension directory name. |
| Resource name | 1–100 characters, for organization administration. |
| Audience | Globally unique, 2–128 characters, matching ^[a-z][a-z0-9-]{1,127}$. Do not use the platform-reserved wamp prefix. |
| Origin | The canonical HTTPS origin only: scheme, host, and optional explicit port. No path, trailing slash, query, fragment, or embedded credentials. |
| Namespace | Globally unique, at most 96 characters, with at least two lowercase dot-separated segments, such as internal.docs. It reserves future capability names even when this integration defines none. |
Configure the deployed service with the values from Account Center and the production values at the top of this page:
WAMP_TOKEN_ISSUER=https://api.vampikez.funWAMP_JWKS_URL=https://api.vampikez.fun/.well-known/jwks.jsonWAMP_RESOURCE_AUDIENCE=internal-docsWAMP_RESOURCE_ORIGIN=https://knowledge.example.comWAMP_RESOURCE_APP_ID=YOUR_APP_IDHOST=0.0.0.0PORT=3000The audience, origin, namespace, and App id are security identifiers, not
display configuration. Once the origin is bound, it cannot be retargeted; use a
new audience for a different production origin. Give staging its own audience
and HTTPS origin as well. Although the MCP URL may include a path such as
/mcp, its new URL(url).origin must equal the registered origin exactly.
List active bindings with GET /api/apps/:slug/resource-servers. Retire one
with DELETE /api/apps/:slug/resource-servers/:resourceServerId. Retirement is
global and irreversible: WAMP stops issuing new tokens, existing tokens drain
within 15 minutes, and the audience and namespace remain reserved so they
cannot silently acquire a new meaning.
Let an organization install the resource
Section titled “Let an organization install the resource”The service backend creates a normal App installation intent with an empty
capability list. Empty means identity only, and the Account API accepts it
only when the requested audience belongs to the App that signed the assertion.
The following Node 22 script creates an intent, or polls one when given its id.
Copy the signing key kid from the App’s key list in Account Center:
npm install jose@6.2.3import { readFile } from 'node:fs/promises';import { importPKCS8, SignJWT } from 'jose';
const required = [ 'WAMP_ACCOUNT_API', 'WAMP_TOKEN_ISSUER', 'WAMP_APP_SLUG', 'WAMP_APP_KID', 'WAMP_APP_PRIVATE_KEY_FILE', 'WAMP_RESOURCE_AUDIENCE',];for (const name of required) { if (!process.env[name]) throw new Error(`Missing ${name}`);}
const key = await importPKCS8( await readFile(process.env.WAMP_APP_PRIVATE_KEY_FILE, 'utf8'), 'EdDSA',);
async function createAssertion() { const now = Math.floor(Date.now() / 1000); return new SignJWT({}) .setProtectedHeader({ alg: 'EdDSA', kid: process.env.WAMP_APP_KID }) .setIssuer(process.env.WAMP_APP_SLUG) .setSubject(process.env.WAMP_APP_SLUG) .setAudience(process.env.WAMP_TOKEN_ISSUER) .setIssuedAt(now) .setExpirationTime(now + 300) .sign(key);}
async function post(path, body) { const response = await fetch(`${process.env.WAMP_ACCOUNT_API}${path}`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body), }); const result = await response.json(); if (!response.ok) throw new Error(`${response.status}: ${JSON.stringify(result)}`); return result.intent;}
const intentId = process.argv[2];const assertion = await createAssertion();const intent = intentId ? await post(`/api/apps/installation-intents/${encodeURIComponent(intentId)}/status`, { assertion, }) : await post('/api/apps/installation-intents', { assertion, resourceAudience: process.env.WAMP_RESOURCE_AUDIENCE, capabilityIds: [], });
console.log(intent);Replace YOUR_KEY_ID, set the remaining values, and create the link:
export WAMP_ACCOUNT_API=https://api.vampikez.funexport WAMP_TOKEN_ISSUER=https://api.vampikez.funexport WAMP_APP_SLUG=internal-knowledgeexport WAMP_APP_KID=YOUR_KEY_IDexport WAMP_APP_PRIVATE_KEY_FILE=./internal-knowledge.private.pemexport WAMP_RESOURCE_AUDIENCE=internal-docsnode installation-intent.mjsSend the returned authorizeUrl to the administrator, then poll with
node installation-intent.mjs <intent-id>. The administrator
signs into WAMP if needed, chooses an organization, and approves disclosure of
its organization identity and one pairwise service-specific user identity.
Store the returned installationId when status becomes authorized. This is
installation consent, not OAuth: the customer creates no account in your
service and copies no key. The exact App assertion contract is in
Apps and credentials.
The status is pending, authorized, or expired. Stop polling on either
terminal status; create a new intent after expired.
Approval is durable for this exact audience and installation. Registering a second resource on the same App does not inherit an older installation or another resource’s approval: create and authorize another identity-only intent. Revoking the App installation revokes every resource approval attached to it.
Installing the connector extension does not silently install the App. Keep the explicit consent link: the extension controls whether the connector exists on a host, while the Account installation controls which organization and members may disclose identity to the service.
An organization administrator can later narrow or revoke the installation through the standard product access controls. No token is issued when the organization, App, installation, membership, or assigned-member access is inactive.
Package the Desktop connector
Section titled “Package the Desktop connector”The connector is a manifest-only WAMP extension. It has no JavaScript,
package.json, SDK dependency, build step, or stored credential. Create a
directory named internal-knowledge-connector containing one file:
{ "name": "Internal Knowledge", "version": "1.0.0", "contributes": { "mcpServers": [ { "id": "knowledge", "transport": "http", "url": "https://knowledge.example.com/mcp", "wampAuth": { "audience": "internal-docs" } } ] }}The directory name is the extension id. To hand the connector to a Desktop user, zip the directory itself:
zip -r internal-knowledge-connector.zip internal-knowledge-connectorIn WAMP Desktop, open Marketplace, choose Add → Import package…, select the zip, and enable the extension. The WAMP operator may curate the same manifest-only package into the public catalog. Installing an extension and approving its App installation are separate actions: both must be present before WAMP can mint identity for the MCP connection.
wampAuth is valid only for http or sse at an HTTPS URL without embedded
credentials. It cannot be combined with oauth or a static Authorization
header. Other static headers are allowed, but they are not identity. The bearer
is obtained and attached by the host; it never enters extension.json,
extension storage, renderer JavaScript, or agent context.
Managed identity may come only from the host-owned global MCP layer, normally
through an installed extension. A repository-owned .wamp/mcp-servers.json is
allowed to declare ordinary MCP servers but cannot set wampAuth. This prevents
a checked out repository from silently requesting the signed-in user’s identity
for an arbitrary service.
wampAuth authenticates the host-managed MCP transport only. Extension UI that
reads your service directly declares auth.resource instead; see
Read your service from extension UI. Never
copy a token into renderer code.
The backend developer hands the extension publisher only two security-relevant
strings: the full MCP endpoint URL and the registered audience. The App id,
JWKS URL, token issuer, and App private key stay in the backend’s configuration;
none belongs in extension.json.
Removing or disabling the extension removes the MCP contribution and its token demand. Your service is therefore optional: a failed or absent connector does not stop WAMP or the agent runtime.
Read your service from extension UI
Section titled “Read your service from extension UI”An extension panel can call your HTTP API as the person viewing it. WAMP Desktop holds the resource identity JWT and sends the request; the panel receives the response, never the token.
Declare the audience and the API base in extension.json:
{ "compat": { "pluginApi": "^4.0.0" }, "requiresElectron": true, "permissions": [ { "auth.resource": [ { "audience": "internal-docs", "baseUrl": "https://docs.example.com/api/v1" } ] } ]}baseUrl is a canonical HTTPS URL with a path and no credentials, query,
fragment or trailing slash. The audience is the one registered for your
resource. An extension may declare up to eight audiences, each once. Keep
requiresElectron until Cloud hosts support these reads.
From the extension UI:
import { pluginAPI } from '@wamp/plugin-api';
const response = await pluginAPI.auth.resourceFetch( { audience: 'internal-docs', path: '/api/v1/documents?limit=20' }, { signal },);// { status, headers: { 'content-type'?, 'retry-after'?, 'x-request-id'? }, body }path is origin-relative, includes the base path and must stay under it. WAMP
refuses a path containing a fragment, whitespace, a control character or a
backslash; a pathname with a . or .. segment or an encoded /, \, . or
%; and a query that does not round-trip through URLSearchParams. It sends
exactly the declared origin followed by path.
Your backend receives one GET with Accept: application/json and
Authorization: Bearer <resource identity JWT>, the same token described in
What WAMP sends. No cookie, Origin or extension-chosen
header is sent.
| Rule | Behavior |
|---|---|
| Methods | GET only |
| Redirects | Not followed; any 3xx fails the call |
| Timeout | 20 seconds |
| Response size | 256 KiB after decompression |
| Response type | application/json or a +json type; anything else fails the call |
401 |
WAMP exchanges a fresh identity once and retries once; a second 401 is returned |
HTTP statuses, including 403, 404, 429 and 5xx, resolve as responses
with their parsed JSON body. A call without a trustworthy response rejects with
a ResourceFetchError whose code is:
code |
Meaning |
|---|---|
not_declared |
The audience is not in this extension’s auth.resource |
invalid_path |
The path breaks the rules above |
signed_out |
Nobody is signed in to WAMP |
access_denied |
Account refused the identity; reason is app_not_installed, member_not_assigned or resource_not_registered when Account names one |
unavailable |
Account or your service could not be reached; retry, after retryAfter seconds when present |
redirect_refused, response_too_large, invalid_response, timeout |
The response broke a rule in the table above |
identity_changed |
The viewer signed out or changed account while the request was out; start over |
host_unsupported |
This host has no renderer resource reads (currently Cloud and the web workspace) |
What WAMP sends
Section titled “What WAMP sends”Every MCP HTTP request carries:
Authorization: Bearer <resource identity JWT>The JWT is Ed25519-signed, has typ: at+jwt, and is short-lived: 15 minutes by
default. Its audience is one exact string.
| Claim | Meaning |
|---|---|
iss |
The exact WAMP platform token issuer supplied during registration. |
aud |
The registered resource audience, for example internal-docs. |
sub |
A pairwise subject. A human subject is stable for this resource App across Desktop and Cloud. An App-installation subject is stable while that source installation exists and changes after it is reinstalled. |
org_id |
The WAMP organization whose installation authorized disclosure. |
subject_kind |
human identifies the execution owner. app_installation identifies a non-human App installation executing a Cloud workload; it is not a WAMP user. |
resource_app_id |
The immutable App that owns the resource server. |
resource_installation_id |
That App’s current installation in org_id. This changes after revocation and reinstallation. |
resource_origin |
The registered origin allowed to receive the token. |
token_use |
Exactly resource_identity. |
iat, nbf, exp, jti |
Issuance window and a unique token id. jti is useful for audit correlation; it is not a single-use nonce. |
There is deliberately no email, name, global WAMP user id, membership id,
parent grant, service permission, or service capability in this token. Store a
local principal under the compound key (iss, org_id, sub). Do not use
resource_installation_id as the user key. Your service may create the local
principal lazily on the first accepted request, so the user never needs a
second registration flow.
Persist subject_kind with that principal. Apply member/user ACLs only to
human; handle app_installation as a service principal with its own ACL, or
deny it if your service does not support non-human callers.
Build a working MCP backend
Section titled “Build a working MCP backend”The following Node 22 service is a complete authenticated Streamable HTTP MCP endpoint with one diagnostic tool. It verifies identity before parsing the MCP body, keeps no WAMP credential, and creates a stateless MCP transport per request. Start with these two files:
{ "name": "internal-knowledge-mcp", "private": true, "type": "module", "scripts": { "start": "node server.mjs" }, "dependencies": { "@modelcontextprotocol/sdk": "1.30.0", "express": "5.2.1", "jose": "6.2.3", "zod": "3.25.76" }}import { createRemoteJWKSet, errors, jwtVerify } from 'jose';
const ISSUER = process.env.WAMP_TOKEN_ISSUER;const JWKS_URL = process.env.WAMP_JWKS_URL;const AUDIENCE = process.env.WAMP_RESOURCE_AUDIENCE;const ORIGIN = process.env.WAMP_RESOURCE_ORIGIN;const APP_ID = process.env.WAMP_RESOURCE_APP_ID;const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
if (!ISSUER || !JWKS_URL || !AUDIENCE || !ORIGIN || !APP_ID) { throw new Error('WAMP resource identity configuration is incomplete');}
const jwks = createRemoteJWKSet(new URL(JWKS_URL));
export class ResourceIdentityAuthorityUnavailableError extends Error {}
function isAuthorityUnavailable(error) { return error instanceof TypeError || error instanceof errors.JWKSTimeout || error instanceof errors.JWKSInvalid || error instanceof errors.JWKInvalid || error?.constructor === errors.JOSEError;}
export async function verifyWampResourceBearer(authorization) { const match = /^Bearer ([^\s]+)$/i.exec(authorization ?? ''); if (!match) throw new Error('missing WAMP resource bearer');
let payload; try { ({ payload } = await jwtVerify(match[1], jwks, { issuer: ISSUER, audience: AUDIENCE, algorithms: ['EdDSA'], typ: 'at+jwt', clockTolerance: 30, requiredClaims: [ 'iss', 'aud', 'sub', 'exp', 'iat', 'nbf', 'jti', 'org_id', 'subject_kind', 'resource_app_id', 'resource_installation_id', 'resource_origin', 'token_use', ], })); } catch (error) { if (isAuthorityUnavailable(error)) { throw new ResourceIdentityAuthorityUnavailableError( 'WAMP resource identity authority is unavailable', { cause: error }, ); } throw error; }
if (payload.aud !== AUDIENCE || payload.token_use !== 'resource_identity' || payload.resource_origin !== ORIGIN || payload.resource_app_id !== APP_ID || (payload.subject_kind !== 'human' && payload.subject_kind !== 'app_installation') || typeof payload.sub !== 'string' || payload.sub.length === 0 || typeof payload.org_id !== 'string' || !UUID.test(payload.org_id) || typeof payload.resource_installation_id !== 'string' || !UUID.test(payload.resource_installation_id)) { throw new Error('invalid WAMP resource identity'); }
return { issuer: payload.iss, organizationId: payload.org_id, subject: payload.sub, subjectKind: payload.subject_kind, installationId: payload.resource_installation_id, tokenId: payload.jti, expiresAt: payload.exp, };}import express from 'express';import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';import { ResourceIdentityAuthorityUnavailableError, verifyWampResourceBearer,} from './resource-auth.mjs';
const host = process.env.HOST ?? '127.0.0.1';const port = Number(process.env.PORT ?? 3000);const app = express();
app.get('/healthz', (_request, response) => { response.json({ ok: true });});
async function authenticate(request, response, next) { try { request.wampPrincipal = await verifyWampResourceBearer( request.get('authorization'), ); next(); } catch (error) { if (error instanceof ResourceIdentityAuthorityUnavailableError) { response .set('Retry-After', '5') .status(503) .json({ error: 'identity_authority_unavailable' }); return; } response .set('WWW-Authenticate', 'Bearer error="invalid_token"') .status(401) .json({ error: 'invalid_token' }); }}
function createServer(principal) { const server = new McpServer({ name: 'internal-knowledge', version: '1.0.0', });
server.registerTool( 'wamp_identity', { description: 'Return the WAMP organization and pairwise service identity.', inputSchema: {}, }, async () => ({ content: [{ type: 'text', text: JSON.stringify({ organizationId: principal.organizationId, subject: principal.subject, subjectKind: principal.subjectKind, }), }], }), );
return server;}
app.post('/mcp', authenticate, express.json({ limit: '1mb' }), async (request, response) => { const server = createServer(request.wampPrincipal); const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); response.on('close', () => { void transport.close(); void server.close(); });
try { await server.connect(transport); await transport.handleRequest(request, response, request.body); } catch (error) { console.error('MCP request failed', error); if (!response.headersSent) { response.status(500).json({ jsonrpc: '2.0', error: { code: -32603, message: 'Internal server error' }, id: null, }); } }});
app.all('/mcp', authenticate, (_request, response) => { response.status(405).json({ jsonrpc: '2.0', error: { code: -32000, message: 'Method not allowed' }, id: null, });});
app.listen(port, host, () => { console.log(`MCP server listening on http://${host}:${port}/mcp`);});Replace YOUR_APP_ID, run npm install, then start locally with
node --env-file=service.env server.mjs. In production, load the same values
through your deployment’s secret/config mechanism. Deploy the final /mcp
endpoint at the exact registered HTTPS origin without a redirect. Replace the
diagnostic tool with your service tools after the identity smoke test. Every
data query must scope its own authorization by principal.organizationId and,
when user-level access matters, principal.subject.
The JWKS URL normally ends in /.well-known/jwks.json. It is public and
cacheable; createRemoteJWKSet caches keys and refreshes on a new kid, so do
not fetch it separately on every request. Keep the issuer, audience, algorithm,
type, origin, App id, and semantic claim checks even though the signature is
valid. Accepting a correctly signed JWT for another WAMP service is still an
authorization bug.
Verify the bearer before parsing or executing the MCP request. Then apply your own ACL:
| Result | HTTP response |
|---|---|
| Missing, malformed, expired, or semantically invalid bearer | 401 and, optionally, WWW-Authenticate: Bearer error="invalid_token" |
| WAMP JWKS cannot be fetched or parsed and no usable cached key exists | 503; never mislabel an authority outage as invalid_token |
| Valid identity without access to the requested document or operation | 403 |
| Valid identity and allowed operation | Continue into the normal MCP handler |
Return real HTTP 401/403 responses rather than a successful JSON-RPC
response containing an authentication error. WAMP uses the HTTP status to
retire a rejected managed connection. It does not launch MCP OAuth for a
wampAuth connector. The user can open Settings → MCP and click Retry
to request a fresh managed token; sign-in, organization switch, and WAMP
credential rotation also rebuild the connection. A persistent 401 remains a
connection error until the service accepts the fresh token or the organization
restores its approval.
Verify the bearer on every HTTP request. For SSE, Streamable HTTP GET, or any
other long-lived response, close the response and its server-side MCP session
no later than the verified exp. WAMP also rotates its client transport before
expiry, but your service must enforce the JWT boundary itself. Offline
verification means installation or membership revocation is bounded by the
token’s remaining lifetime.
Rotation retires the managed transport before expiry, so a tool call still in flight at that boundary can end with a transport error. Make mutating tools idempotent and safe to retry; WAMP does not silently replay an ambiguous call.
Do not redirect an authenticated MCP request. WAMP refuses every 3xx on this
path so a bearer cannot cross origins, and it does not replay an ambiguous
mutating MCP request. Serve the final MCP endpoint at the URL in the manifest.
Runtime behavior after the connector is present
Section titled “Runtime behavior after the connector is present”| Runtime | Identity and network path |
|---|---|
| Desktop | WAMP silently exchanges the current Desktop OIDC grant. The local engine calls your MCP URL from the user’s machine. Login, logout, organization switch, or credential rotation retires the old identity-bound connection. |
| Cloud native agent | When the connector manifest is included in the Cloud engine image, the control plane reauthorizes the Session’s durable execution owner and installs the JWT in the sandbox engine’s memory. The sandbox or organization runner calls your MCP URL directly; the Account service is not a traffic proxy. |
| Cloud ACP agent | With that same image requirement, the sandbox engine connects to your service, discovers the tool, and lends a credential-free WAMP MCP facade to the ACP process. Tool calls return through that facade. The ACP agent never receives your URL credential or direct upstream connection. |
Installing a connector from Desktop Marketplace does not currently copy it into managed Cloud sandboxes. A WAMP operator must add its unchanged manifest to the Cloud engine image. After that delivery step, your DNS, TLS, firewall, and routing must work from the selected sandbox runner. A private service reachable only on an organization’s network can use that organization’s self-hosted runner; managed sandboxes need a route to the service. Network source addresses are not identity — always authorize the JWT.
Cloud keeps the Session execution owner as the connected-resource subject. Sharing a Session intentionally shares that sandbox’s capabilities; unrelated Sessions and organizations do not reuse the same lease. Design agent behavior so an optional or temporarily unavailable tool may be absent.
A Cloud workspace may require at most eight distinct managed
(audience, origin) pairs. On the first tool snapshot for a new configuration
or credential set, WAMP waits up to five seconds for their MCP discovery; a
slower or failed optional server is absent from that snapshot and continues
connecting in the background.
Test the integration
Section titled “Test the integration”- Register a staging audience and HTTPS origin in Account Center. Create an
identity-only installation link and approve it for a test organization. Keep
ALL_MEMBERSfor the first smoke test. - Deploy the example MCP service at the exact manifest URL and configure the
five service values listed above. Confirm
/healthzanswers before involving WAMP. - Import the connector zip through Marketplace → Add → Import package…, sign into WAMP Desktop, select the test organization, and enable the extension. You do not extract or paste a bearer.
- Ask a native agent to call
wamp_identity. Confirm the service sees the expectediss,org_id, pairwisesub,resource_app_id, andresource_origin. - Run the same call through a Desktop ACP runtime whose manifest lends the
mcptool category. Confirm the service sees the same principal and no ACP credential. - If a WAMP operator has included the connector in the Cloud image, run the same native and ACP calls from a Cloud Session. This is not a self-service extension installation step today.
- Change the installation to
ASSIGNED_MEMBERS, exclude the test member, and confirm a fresh Desktop login or Cloud Session cannot obtain the tool. A bearer already issued to an existing Session remains valid only until itsexp.
Log claim identifiers and authorization decisions, never the bearer. Treat the
pairwise sub as a pseudonymous user identifier and protect it like other
account data.