14. Usage examples
These examples show how an application, agent, renderer, and MCP client share one capability contract. Each example begins with a concrete product problem, identifies the library boundary involved, and then shows a representative implementation.
Choose an example below according to the product problem you are solving. Each card explains what the pattern is for and which part of the library it exercises.
Standalone code convention: The reference examples below are independent files. Every snippet declares its imports, types, local values, policy, call path, and result handling. Comments identify where CUP is making the authorization decision. Replace the illustrative adapter implementations with production integrations.
14.0 Standalone reference implementations
These complete samples focus on use rather than registration. Agent examples use an external runtime supplied by the host; CUP only supplies identity, projections, policy decisions, and guarded capability access. Copy any one into its own TypeScript file. Each sample creates the CUP instance it needs and calls a real library operation from request to result.
14.0.1 Read filtered data
Use case: A user searches CRM contacts. CUP separates discovery from reading, binds access to a workspace, limits the query, and redacts fields before returning data.
import {
createCapabilityUI,
memoryRegistry,
policy,
subject,
resource,
type Subject,
type RequestContext
} from '@capability-ui/core';
// This fake adapter stands in for a database-backed ResourceAdapter.
const contacts = [
{ id: 'c1', workspaceId: 'acme', name: 'Ada', company: 'Acme', email: '
[email protected]', phone: '555-0100' },
{ id: 'c2', workspaceId: 'other', name: 'Lin', company: 'Other', email: '
[email protected]', phone: '555-0101' }
];
const registry = memoryRegistry();
registry.register({
id: 'crm.contacts', type: 'data', version: '1.0',
sensitivity: 'confidential', schema: contactSchema,
read: async ({ scope, query }) => contacts.filter(c =>
c.workspaceId === scope.workspaceId && c.name.includes(query.search)
)
});
const cup = createCapabilityUI({
registry,
policy: policy.denyByDefault(),
receipts: inMemoryReceipts()
});
// CUP allows the sales role to read only its own workspace.
cup.policy.allow({
id: 'sales-contact-read', principal: { role: 'sales' },
operation: 'read', resource: resource('crm.contacts'),
scope: { workspaceId: { fromSubject: 'workspaceId' } },
obligations: [{ type: 'redact', fields: ['phone'] }]
});
const user: Subject = {
id: 'user:ada', type: 'user', authenticated: true,
attributes: { role: 'sales', workspaceId: 'acme' }
};
const context: RequestContext = {
purpose: 'contact_lookup', channel: 'web'
};
async function main() {
// Discovery asks whether the user may know that the resource exists.
const available = await cup.discover({ subject: user, purpose: context.purpose, context });
// Read performs a second decision, applies scope, then redacts phone numbers.
const result = await cup.read({
subject: user, resource: 'crm.contacts',
query: { search: 'Ada', limit: 10 },
fields: ['id', 'name', 'company', 'email'],
scope: { workspaceId: user.attributes.workspaceId },
purpose: context.purpose, context
});
console.log({ available, contacts: result.items, receiptId: result.receiptId });
}
void main();
14.0.2 Execute a confirmed tool
Use case: A user sends an email. CUP validates the arguments, requires explicit confirmation, binds that confirmation to the exact payload, and records the result.
import {
createCapabilityUI,
memoryRegistry,
policy,
subject,
capability,
type Subject
} from '@capability-ui/core';
const cup = createCapabilityUI({
registry: memoryRegistry(),
policy: policy.denyByDefault(),
receipts: postgresReceipts(process.env.DATABASE_URL)
});
// The schema prevents malformed recipients and the metadata tells the UI
// that this action creates an external side effect.
cup.register(capability({
id: 'mail.send', operation: 'execute', inputSchema: mailSchema,
outputSchema: receiptSchema, risk: 'high',
sideEffects: ['external_message'], confirmation: 'explicit',
idempotency: 'required',
handler: async (input, ctx) => mailProvider.send(input, ctx.idempotencyKey)
}));
const user: Subject = subject('user:john', { roles: ['owner'] });
cup.policy.allow({
id: 'owner-send-mail', principal: user,
operation: 'execute', resource: capability('mail.send'),
conditions: [purposeIs('job_outreach'), recipientCountAtMost(20)],
obligations: [requireConfirmation('explicit'), writeReceipt()]
});
async function main() {
const input = {
from: '
[email protected]', to: ['
[email protected]'],
subject: 'Product role', body: 'Hello from John.'
};
// Prepare normalizes the input and computes the hash that confirmation signs.
const prepared = await cup.prepare({
subject: user, capability: 'mail.send', input,
purpose: 'job_outreach', context: { channel: 'web' }
});
console.log('Review before sending:', prepared.preview);
// The UI must confirm the exact prepared payload, not a paraphrase of it.
const confirmation = await userConfirm({
inputHash: prepared.inputHash, sideEffects: prepared.sideEffects
});
// Execute rechecks policy immediately before the provider is called.
const receipt = await cup.execute({
...prepared.request, confirmation,
idempotencyKey: 'job-outreach-2026-01'
});
console.log('Final receipt:', receipt);
}
void main();
14.0.3 Run a bounded single-agent task
Use case: An assistant prepares a meeting brief. The agent may choose steps, but CUP decides which resources and actions are available on every iteration and enforces a read budget.
import {
createCapabilityUI,
memoryRegistry,
policy,
subject,
type AuthorizedView,
type AgentAction
} from '@capability-ui/core';
// External runtime: CUP does not provide this implementation.
import { createRuntime } from 'any-existing-agent-framework';
const cup = createCapabilityUI({
registry: productionRegistry(),
policy: productionPolicy(),
receipts: postgresReceipts(process.env.DATABASE_URL)
});
const assistant = subject('agent:personal', { owner: 'user:john' });
// The host supplies an agent from its chosen framework.
const agent = createRuntime({ model: 'configured-by-host' });
async function main() {
// Projection limits the agent's initial knowledge to this goal and purpose.
const view: AuthorizedView = await cup.project({
subject: assistant,
goal: 'prepare tomorrow meeting brief',
context: { purpose: 'meeting_brief', channel: 'web' }
});
let state = { facts: [], readsUsed: 0, receipts: [] };
for (let step = 0; step < 8; step += 1) {
// The model can propose an action, but it cannot invent authority.
const proposal: AgentAction = await agent.planNext({
goal: 'prepare tomorrow meeting brief', state, authorizedView: view
});
if (proposal.kind === 'final') break;
// CUP re-authorizes each step against current policy and the remaining budget.
const receipt = await cup.execute({
subject: assistant, capability: proposal.capability,
input: proposal.input, purpose: 'meeting_brief',
context: { budget: { reads: 20 - state.readsUsed } }
});
state = reduceAgentState(state, receipt);
// A denial becomes a typed constraint for the next planning step.
if (receipt.status === 'denied') {
state = addConstraint(state, receipt.decision.reasonCode);
}
}
console.log(await agent.summarize(state));
}
void main();
14.0.4 Coordinate delegated research agents
Use case: A coordinator asks separate agents to scan public sources and private research notes. Each worker receives attenuated authority, a separate identity, a purpose, and an expiration.
import {
createCapabilityUI,
productionRegistry,
productionPolicy,
subject,
type DelegationGrant
} from '@capability-ui/core';
import { runWorker } from '@agent-workers/runtime';
const cup = createCapabilityUI({
registry: productionRegistry(), policy: productionPolicy(),
receipts: postgresReceipts(process.env.DATABASE_URL)
});
const user = subject('user:john');
const coordinator = subject('agent:research-coordinator');
async function main() {
// Each grant is narrower than John's authority and expires quickly.
const webGrant: DelegationGrant = await cup.delegate({
from: user, to: subject('agent:web-worker'), capability: 'web.search',
attenuation: {
purpose: 'market_scan', expiresIn: '15m', operations: ['execute'],
scope: { domains: ['public-data.org'] }
}
});
const notesGrant: DelegationGrant = await cup.delegate({
from: user, to: subject('agent:notes-worker'), capability: 'notes.read',
attenuation: {
purpose: 'market_scan', expiresIn: '15m', operations: ['read'],
scope: { notebook: 'research', fields: ['title', 'body'] }
}
});
// Workers call through CUP; they never receive direct database credentials.
const findings = await Promise.all([
runWorker({ grant: webGrant, goal: 'find public market evidence',
call: (capability, input) => cup.execute({
subject: webGrant.to, delegation: webGrant, capability, input,
purpose: 'market_scan', context: {}
}) }),
runWorker({ grant: notesGrant, goal: 'find relevant internal notes',
call: (capability, input) => cup.execute({
subject: notesGrant.to, delegation: notesGrant, capability, input,
purpose: 'market_scan', context: {}
}) })
]);
// Worker output is untrusted content before synthesis.
console.log(await synthesizeFor(coordinator, sanitizeUntrusted(findings)));
}
void main();
14.0.5 Serve the same capabilities through MCP
Use case: An MCP client connects to a CUP-backed server. The server lists only session-authorized resources and tools, then sends every call through the same execution guard used by web and native clients.
import { createCapabilityUI, productionRegistry, productionPolicy } from '@capability-ui/core';
import { createMCPServer } from '@capability-ui/mcp';
import { verifyOIDC } from '@company/identity';
const cup = createCapabilityUI({
registry: productionRegistry(), policy: productionPolicy(),
receipts: postgresReceipts(process.env.DATABASE_URL)
});
const server = createMCPServer({
name: 'john-workspace', cup,
authenticate: async request => verifyOIDC(request.headers.authorization),
transports: ['streamable_http']
});
// resources/list and tools/list call cup.project() for the authenticated session.
// They never dump the host registry to the MCP client.
server.on('resources/list', async session => cup.project({
subject: session.subject, goal: session.goal, context: session.context
}));
// tools/call resolves one immutable capability and delegates enforcement to CUP.
server.on('tools/call', async (session, call) => cup.execute({
subject: session.subject, capability: call.cupCapabilityId,
input: call.arguments, purpose: session.purpose, context: session.context
}));
await server.listen({ http: { port: 8787, path: '/mcp' } });
14.0.6 Use a remote MCP server inside an agent task
Use case: An agent uses a remote planning service, but the local CUP host controls whether that remote tool is visible and executable. Remote authority is namespaced and attenuated.
import {
createCapabilityUI,
productionRegistry,
productionPolicy,
subject
} from '@capability-ui/core';
import { connectMCP } from '@capability-ui/mcp-client';
// External runtime: CUP does not provide this implementation.
import { createRuntime } from 'any-existing-agent-framework';
const cup = createCapabilityUI({
registry: productionRegistry(), policy: productionPolicy(),
receipts: postgresReceipts(process.env.DATABASE_URL)
});
const agentSubject = subject('agent:personal', { owner: 'user:john' });
// The host supplies an agent from its chosen framework.
const agent = createRuntime({ model: 'configured-by-host' });
async function main() {
// Mounting imports schemas; it does not grant execution permission.
const remote = await connectMCP({
url: 'https://planning.example/mcp',
auth: process.env.MCP_OIDC_TOKEN,
serverName: 'planning'
});
await cup.mount(remote, { namespace: 'planning_remote' });
// The local projection filters the remote tools for this subject and goal.
const view = await cup.project({
subject: agentSubject, goal: 'create a launch plan',
context: { purpose: 'project_planning' }
});
const plan = await agent.plan({ goal: 'create a launch plan', tools: view.capabilities });
for (const action of plan.actions) {
// This local check runs before the MCP adapter forwards the call remotely.
const receipt = await cup.execute({
subject: agentSubject, capability: action.capability,
input: action.input, purpose: 'project_planning', context: {}
});
if (receipt.status !== 'succeeded') {
console.error('CUP stopped action:', receipt.decision.reasonCode);
break;
}
}
}
void main();
14.1 Minimal application setup
What it is: The smallest host application that creates a registry, policy engine, and receipt sink.
Purpose: Give a developer a starting point for adopting CUP without changing the existing database or agent.
How CUP is applied: The host registers a resource with an identity, schema, owner, and version. It starts from deny-by-default, then adds one explicit policy allowing John to read that resource. Later projections can expose only this authorized resource, while receipts provide the audit boundary.
import { createCapabilityUI, memoryRegistry, denyByDefault } from '@capability-ui/core';
const cup = createCapabilityUI({
registry: memoryRegistry(),
policy: denyByDefault(),
receipts: postgresReceipts(process.env.DATABASE_URL),
clock: systemClock()
});
cup.register({
id: 'notes.john', type: 'data', version: '1.0',
sensitivity: 'personal', owner: 'user:john',
schema: noteSchema
});
cup.policy.allow({
id: 'john-read-notes', principal: subject('user:john'),
operation: 'read', resource: resource('notes.john'),
scope: { fields: ['id', 'title', 'body'] }, priority: 100
});
14.2 Expose a database as filtered data
What it is: A CRM resource whose rows and fields are constrained by workspace and role.
Purpose: Show that permission applies to the returned data, not merely to a database endpoint.
How CUP is applied: The resource adapter loads data only for the subject’s workspace. The policy matches the sales role, binds the workspace to a subject attribute, and attaches a redaction obligation. CUP evaluates the request first, then removes unauthorized fields before the renderer or agent receives the result.
cup.register(dataResource({
id: 'crm.contacts', version: '3.2', sensitivity: 'confidential',
schema: contactSchema,
read: async ({ scope }) => db.contacts.findMany({
where: { workspaceId: scope.workspaceId },
select: { id: true, name: true, company: true, email: true }
})
}));
cup.policy.allow({
id: 'sales-read-team-contacts',
principal: role('sales'), operation: 'read',
resource: resource('crm.contacts'),
scope: { workspaceId: fromSubject('workspaceId') },
obligations: [redact(['phone', 'personalEmail'])]
});
const view = await cup.project({
subject: alice, goal: 'find contacts for the launch',
context: { workspaceId: 'acme', purpose: 'sales' }
});
14.3 Turn a capability into a generated form
What it is: A calendar capability rendered into a form from its input schema.
Purpose: Let a renderer create useful controls without a hand-built screen for every action.
How CUP is applied: CUP projects the capability only when the user may inspect or execute it. The renderer reads the authorized input schema, builds controls, displays the declared external commitment, and attaches the confirmation obligation. The renderer creates presentation; CUP still decides whether the submitted event may be created.
const capability = defineCapability({
id: 'calendar.create_event', operation: 'execute',
inputSchema: createEventSchema, outputSchema: eventSchema,
sideEffects: ['external_commitment'], risk: 'medium',
confirmation: 'explicit', reversibility: 'reversible'
});
cup.register(capability);
const view = await cup.project({ subject: john, goal: 'schedule a team meeting' });
const action = view.resources
.flatMap(r => r.capabilities)
.find(c => c.id === 'calendar.create_event');
// A renderer chooses the visual form from action.inputSchema.
return renderCapabilityForm(action, {
showSideEffects: true,
showConfirmation: action.obligations.includes('require_confirmation')
});
14.4 Protect a side-effecting tool
What it is: An email sender with high risk, explicit confirmation, and idempotency.
Purpose: Demonstrate the safe path for actions that contact other people or create external commitments.
How CUP is applied: The capability declares its side effect, risk, confirmation mode, and need for an idempotency key. CUP checks the purpose, recipient limit, approval, schema, and exact input hash immediately before invoking the mail provider. If any condition changes, execution is denied and the adapter is never called.
cup.register(defineCapability({
id: 'mail.send', operation: 'execute', inputSchema: mailSchema,
outputSchema: receiptSchema, sideEffects: ['external_message'],
risk: 'high', confirmation: 'explicit', idempotency: 'required',
handler: async (input, ctx) => mailProvider.send(input, ctx.idempotencyKey)
}));
cup.policy.allow({
id: 'john-send-job-outreach', principal: subject('user:john'),
operation: 'execute', resource: capability('mail.send'),
conditions: [purposeIs('job_outreach'), recipientCountAtMost(20)],
obligations: [requireConfirmation('explicit'), writeReceipt()]
});
// The executor, not the button, is the security boundary.
const receipt = await cup.execute({
subject: john, capability: 'mail.send',
input: { from: '
[email protected]', to: recipients, body },
purpose: 'job_outreach', confirmation: userConfirmation,
idempotencyKey: crypto.randomUUID(), context
});
14.5 Build a read-only personal assistant
What it is: A personal assistant limited to an authorized view of notes and briefing data.
Purpose: Give an agent enough information to be useful while withholding the user’s full registry and write actions.
How CUP is applied: The user’s goal and purpose become projection inputs. CUP returns readable note fields and only the capabilities allowed for a personal briefing. The agent receives that bounded view instead of the host registry. Even a read operation passes through CUP so field filters and current policy remain effective.
const authorized = await cup.project({
subject: assistantFor(john),
goal: 'prepare my morning briefing',
context: { purpose: 'personal_briefing', channel: 'telegram' }
});
// The agent sees schemas and permitted actions, not the whole registry.
const answer = await agent.run({
instruction: 'Prepare the briefing from the authorized view.',
tools: authorized.resources.flatMap(r => r.capabilities),
data: authorized.resources.filter(r => r.visibility === 'readable')
});
// Read actions still pass through the executor for field redaction.
const notes = await cup.read({
subject: assistantFor(john), resource: 'notes.john',
fields: ['title', 'body'], context: { purpose: 'personal_briefing' }
});
14.6 Share a capability with attenuation
What it is: A user grants an agent a narrower version of a search capability.
Purpose: Demonstrate safe delegation without handing the agent the user’s full authority.
How CUP is applied: CUP records the delegator, recipient, capability, purpose, expiration, fields, and allowed operations. The effective policy is the intersection of the original grant and the attenuation. At execution, CUP verifies the delegation chain and refuses any input that exceeds those limits.
const grant = await cup.delegate({
from: john,
to: subject('agent:research'),
capability: 'crm.contacts.search',
attenuation: {
scope: { workspaceId: 'acme', fields: ['name', 'company'] },
purpose: 'market_research', expiresIn: '30m',
operations: ['execute']
}
});
// Effective authority is the intersection of all grants.
// The delegated agent cannot widen fields, purpose, time, or operations.
await cup.execute({
subject: subject('agent:research'), capability: 'crm.contacts.search',
delegation: grant, input: { query: 'healthcare startups' }, context
});
14.7 Publish CUP capabilities through MCP
What it is: A CUP host exposes its authorized resources and tools as an MCP server.
Purpose: Make the library interoperable with MCP clients while keeping CUP as the permission authority.
How CUP is applied: The server authenticates the MCP session, builds a subject-specific projection, and maps only that projection to MCP resources and tools. On tools/call, the adapter resolves the MCP name, validates arguments, invokes cup.execute(), and writes a receipt. MCP changes the wire format; it does not bypass CUP.
import { createMCPServer } from '@capability-ui/mcp';
const server = createMCPServer({
name: 'john-workspace',
cup,
authenticate: async request => oidc.verify(request),
transports: ['streamable_http', 'stdio']
});
// resources/list, resources/read, tools/list, and tools/call
// are generated from the session's authorized CUP projection.
await server.listen({ http: { port: 8787, path: '/mcp' } });
The MCP server must not call registry.listAll() and pass the result to the client. Its handlers call cup.project() for listing and cup.execute() for invocation.
14.8 Consume an MCP server through the same library
What it is: A local CUP host mounts tools from another MCP server under a namespace.
Purpose: Combine local policy with remote capabilities without treating a remote tool as trusted by default.
How CUP is applied: The MCP client adapter imports remote schemas as namespaced capabilities. The local policy engine decides whether they are discoverable, inspectable, or executable for this subject and purpose. The local executor rechecks the request and constrains the remote call before forwarding it.
import { connectMCP } from '@capability-ui/mcp-client';
const remote = await connectMCP({
url: 'https://tools.example.com/mcp',
subject: john,
auth: oidcToken,
purpose: 'team_planning'
});
// Remote tools enter the local registry as namespaced capabilities.
await cup.mount(remote, { namespace: 'company_tools' });
const view = await cup.project({ subject: john, goal: 'plan the launch' });
const result = await cup.execute({
subject: john, capability: 'company_tools.release.create_plan',
input: { project: 'launch-2027' }, context
});
14.9 Subscribe to authorized changes
What it is: A client subscribes to project updates and rechecks access for every event.
Purpose: Prevent a previously authorized subscription from becoming a data leak after revocation.
How CUP is applied: CUP authorizes the initial subscription, but does not treat that decision as permanent. Each event is checked against the current policy and resource version. Allowed updates reach the renderer; revoked access produces removal or invalidation instead of leaking the new data.
const subscription = await cup.subscribe({
subject: john, resource: 'projects.acme',
events: ['updated'], context
});
subscription.on('event', async event => {
// Re-authorize every event. Revocation produces 'access_removed'.
const decision = await cup.authorize({
subject: john, operation: 'read', resource: event.resource,
context
});
if (decision.effect === 'allow') renderer.update(event.data, decision);
else renderer.remove(event.resource);
});
14.10 Test the entire boundary
What it is: An integration test that attempts to execute a hidden, unauthorized mail capability.
Purpose: Turn the security promise into an executable contract.
How CUP is applied: The test checks both layers: projection does not disclose the capability, and execution independently evaluates the forged request. The expected denial is meaningful only when the mail adapter remains untouched, proving that the enforcement boundary is outside the generated UI.
it('cannot execute a hidden or unauthorized capability', async () => {
const view = await cup.project({ subject: otherUser, goal: 'send mail' });
expect(view.resources.some(r => r.ref.id === 'mail.send')).toBe(false);
await expect(cup.execute({
subject: otherUser, capability: 'mail.send',
input: { to: ['
[email protected]'], body: 'hello' }, context
})).rejects.toMatchObject({ code: 'CUP_NOT_AUTHORIZED' });
expect(mailProvider.send).not.toHaveBeenCalled();
});
14.11 Read authorized data at runtime
What it is: A user asks an agent to search and read a bounded set of records.
Purpose: Show the runtime path after setup: discover what exists, inspect the schema, query within scope, and receive redacted data.
How CUP is applied: The agent never reads the database directly. CUP authorizes discovery and reading separately, constrains the query fields and workspace, applies pagination limits, and returns a receipt for the read operation.
const visible = await cup.discover({
subject: john, purpose: 'personal_search', context
});
const contacts = visible.find(r => r.ref.id === 'crm.contacts');
const page = await cup.read({
subject: john,
resource: 'crm.contacts',
query: { search: 'Acme', cursor: null, limit: 25 },
fields: ['id', 'name', 'company'],
scope: { workspaceId: 'acme' },
purpose: 'personal_search', context
});
console.log(page.items); // filtered records only
console.log(page.nextCursor); // opaque cursor, if more data exists
console.log(page.receiptId); // audit reference
14.12 Call a capability through prepare, confirm, execute
What it is: A calendar action is selected from the authorized view and executed after the user reviews it.
Purpose: Show the complete call protocol instead of stopping at capability registration.
How CUP is applied: prepare() validates the schema and produces a normalized preview. The UI displays the exact side effect. Confirmation is bound to that normalized input. execute() rechecks policy and rejects a changed payload.
const prepared = await cup.prepare({
subject: john,
capability: 'calendar.create_event',
input: { title: 'Launch review', start, end, attendees },
purpose: 'team_planning', context
});
// Renderer shows prepared.preview and prepared.obligations.
const confirmation = await user.confirm({
title: prepared.preview.title,
changes: prepared.preview.changes,
risks: prepared.obligations
});
const receipt = await cup.execute({
...prepared.request,
confirmation, // bound to input hash
idempotencyKey: 'launch-review-v1'
});
if (receipt.status === 'succeeded') showSuccess(receipt);
else showActionableError(receipt.decision.reason);
14.13 A single agent with a bounded action loop
What it is: An agent plans a multi-step task, such as preparing a meeting brief, using only capabilities in its authorized projection.
Purpose: Show how CUP constrains an agent throughout a task rather than authorizing the entire task once at the beginning.
How CUP is applied: The host creates a projection for the goal. The agent proposes one step at a time. Each step is checked against the current policy, resource version, purpose, and budget. A denied step is returned to the agent as a typed result, not silently substituted with a broader capability.
const view = await cup.project({
subject: assistantFor(john),
goal: 'prepare tomorrow meeting brief',
context: { purpose: 'meeting_brief', budget: { reads: 20, writes: 0 } }
});
let state = { goal, facts: [], receipts: [] };
for (let step = 0; step < 8; step++) {
const proposal = await agent.planNext({ state, authorizedView: view });
if (proposal.kind === 'final') break;
const result = await cup.execute({
subject: assistantFor(john), capability: proposal.capability,
input: proposal.input, purpose: 'meeting_brief', context,
budget: state.budget
});
state = reduce(state, result);
if (result.status === 'denied') {
state = addConstraint(state, result.decision.reasonCode);
}
}
return agent.summarize(state);
14.14 Multi-agent research team
What it is: A coordinator agent delegates narrow research tasks to specialist agents and combines their outputs.
Purpose: Demonstrate that agents can collaborate without sharing the user’s entire data set or unrestricted tools.
How CUP is applied: The coordinator receives a broad but bounded authority. It creates attenuated grants for search-only workers with separate purposes and expiration times. Each worker is a distinct principal. Their outputs are treated as untrusted data, and the coordinator cannot grant permissions it does not possess.
const coordinator = subject('agent:research-coordinator');
const workers = await Promise.all([
cup.delegate({ from: john, to: subject('agent:market-worker'),
capability: 'web.search', attenuation: {
purpose: 'market_scan', expiresIn: '15m', operations: ['execute'],
scope: { domains: ['example.com', 'public-data.org'] }
}}),
cup.delegate({ from: john, to: subject('agent:notes-worker'),
capability: 'notes.read', attenuation: {
purpose: 'market_scan', expiresIn: '15m', operations: ['read'],
scope: { fields: ['title', 'body'], notebook: 'research' }
}})
]);
const findings = await Promise.all(workers.map(grant => worker.run({
grant, goal: 'find evidence for the market scan',
call: (capability, input) => cup.execute({
subject: grant.to, delegation: grant, capability, input,
purpose: 'market_scan', context
})
})));
return coordinator.synthesize({ findings: sanitizeUntrusted(findings) });
14.15 Agent using a remote MCP server
What it is: An agent uses a remote MCP server to create a project plan while CUP remains the local authority for what the agent may invoke.
Purpose: Show how MCP fits into an agent loop without turning remote tool discovery into automatic trust.
How CUP is applied: Remote tools are mounted under a namespace, inspected, and projected like local capabilities. The agent sees only permitted tools. Every remote call passes local policy, input validation, confirmation rules, and receipt creation before the MCP adapter forwards it.
const remote = await connectMCP({
url: 'https://planning.example/mcp', auth: oidcToken,
subject: assistantFor(john), purpose: 'project_planning'
});
await cup.mount(remote, { namespace: 'planning_remote' });
const view = await cup.project({
subject: assistantFor(john), goal: 'create a launch plan',
context: { purpose: 'project_planning' }
});
const plan = await agent.plan({ goal, tools: view.capabilities });
for (const action of plan.actions) {
const receipt = await cup.execute({
subject: assistantFor(john), capability: action.capability,
input: action.input, purpose: 'project_planning', context
});
if (receipt.status !== 'succeeded') return recoverFromDecision(receipt);
}
14.16 Adaptive interface for different principals
What it is: The same product renders different controls for an owner, a read-only collaborator, and a public visitor.
Purpose: Show the practical GenUI result of CUP: one resource model, many interfaces, with differences driven by policy rather than hard-coded role screens.
How CUP is applied: Each principal receives a different projection. The renderer displays read controls, edit controls, or an existence-only summary according to the returned capabilities. If a user changes roles while the page is open, the next execution is checked against the new policy.
const view = await cup.project({
subject: currentSubject,
goal: 'work with project acme', context
});
return render(view, {
resource: 'projects.acme',
componentFor(resource) {
if (resource.capabilities.some(c => c.id === 'projects.update'))
return ProjectEditor;
if (resource.visibility === 'readable')
return ProjectReader;
if (resource.visibility === 'listed')
return ProjectSummary;
return null;
}
});
Implementation rule: every example uses the same core sequence: register, authorize, project, compose, confirm when required, re-authorize, execute, and receipt. MCP changes the wire protocol, not that sequence.