A practical architecture and implementation guide using the fictional Atlas Workspace project.
The problem we wanted to solve
Imagine developing Atlas Workspace across three local coding sessions: one builds an analytics engine, another implements a workflow editor, and a third runs a long data replay. Your dot lives in a conversation outside those sessions. You want to ask, “Are they on track?”, read their results, and, when explicitly authorized, send a specific instruction to the right session.
Copying messages manually works initially. It becomes unreliable when sessions restart, goals change, long operations outlive their initiating conversation, or a network timeout leaves you unsure whether an instruction arrived.
The solution is a small, authenticated control plane between the assistant and the local development runtime. OAuth establishes who may access the bridge. Project admission, session ownership, permission checks, and durable operation records decide what may happen after access is granted.
This article combines observed bridge behavior and reliability lessons with a generalized implementation blueprint. Atlas Workspace, its endpoints, identifiers, scopes, and code examples are fictional. Suggested storage and deployment choices are recommendations, not a claim about the original implementation's exact stack. This is not a universal built-in capability of every assistant client: the client, connector, and local runtime must support the necessary interfaces.
1 Understand the architecture before writing code
The system has five logical parts:
- The dot and its tool client. They interpret the user's request and invoke structured tools rather than typing into another chat blindly.
- An OAuth authorization server. It handles authentication, consent, and token issuance.
- The bridge's protected MCP endpoint. It validates requests and exposes a deliberately small workflow API.
- A local runtime adapter. It talks to the supported session runtime and translates native state into stable bridge responses.
- Durable control state. It records admitted sessions, ownership, goal bindings, operation identities, delivery evidence, and recovery state.
The conceptual path is:
User → dot → authenticated MCP request → bridge policy checks → local runtime adapter → exact session.
Results travel back through the adapter and bridge with provenance. OAuth redirects are a separate browser-based connection flow; they are not the path used to execute session instructions.
“Direct” means the bridge can address the native session instead of relying on a separate conversational orchestrator to forward messages. It does not mean bypassing authentication, ownership, or user permissions. A direct bridge can still include queues and durable recovery logic.
2 Separate three kinds of authority
A reliable design distinguishes:
- Connection authority: may this authenticated principal call the bridge?
- Resource authority: may that principal see or control this project and session?
- Action authority: did the user authorize this particular kind of operation under the current policy?
A valid token is necessary but insufficient. A user can grant read-only monitoring while withholding permission to resume or steer work. A session can belong to the correct repository but currently be controlled by a desktop client. A requested instruction can be permitted in principle but blocked by an unresolved previous delivery.
For Atlas Workspace, the default monitoring path should remain read-only. It can inspect goals, recent messages, test results, and pending approvals. It should not silently acquire a writer, start a model turn, or change permissions merely because the user asks for a status report.
The bridge must enforce these boundaries in code. Tool descriptions help the assistant choose correctly, but descriptions alone are not a security boundary.
3 Build the OAuth connection layer
In HTTP-based MCP authorization, the MCP server is a protected resource server and the client obtains an access token from an authorization server. The current specification uses protected-resource discovery and authorization-server discovery; implementation compatibility must be checked against the protocol version supported by both sides. The July 2026 specification also distinguishes Client ID Metadata Documents, preregistration, and legacy dynamic registration. Do not assume an older registration tutorial remains the preferred approach. MCP authorization specification
For an illustrative deployment, assign a public HTTPS resource such as https://bridge.example.com/mcp. This hostname is an example, not a real connection destination. The local runtime itself does not need to expose an unrestricted public administrative port. Choose a supported, authenticated transport between the protected endpoint and the local adapter, then document that transport separately.
Discovery and configuration
Publish protected-resource metadata identifying the bridge resource and its authorization server. A client can discover the applicable metadata through the standardized mechanisms, including a challenge response. Metadata describes the resource; it is not an access grant. Verify identifiers consistently rather than allowing arbitrary caller-supplied URLs to select a different server. OAuth Protected Resource Metadata, RFC 9728
Configure a supported OAuth client registration method, exact redirect destinations, authorization and token endpoints, and the scopes your bridge will enforce. Follow the host platform's actual connection requirements instead of inventing callback URLs or assuming all clients share the same registration behavior. OpenAI's plugin authentication guidance describes supported registration and PKCE integration options. OpenAI plugin authentication
Authorization code and PKCE
The client creates a fresh PKCE verifier and derives its challenge. The browser authorization request carries the challenge; the subsequent code exchange presents the verifier. The authorization server checks that they match. This binds the exchange to the initiating client and reduces the usefulness of an intercepted authorization code. Use the supported secure challenge method rather than treating PKCE as an optional decoration. PKCE, RFC 7636
At a high level, the connection proceeds as follows:
- The client discovers the bridge's authorization requirements.
- The user signs in and reviews the requested permissions.
- The authorization server returns an authorization code to the registered client redirect.
- The client exchanges the code using PKCE and any required client authentication.
- The client presents an access token to the bridge.
- The bridge validates that token and maps it to its own authorized principal and resources.
Use maintained OAuth components, validate redirects and issuer relationships, protect tokens, and implement appropriate expiration and revocation handling. Do not put tokens in prompts, receipts, source control, screenshots, or diagnostic output. OAuth security guidance discourages insecure grant patterns and describes protections against interception, replay, and mix-up attacks. OAuth Security Best Current Practice, RFC 9700
Audience and scopes
A token intended for another service must not become a bridge credential. Resource indicators let a client identify the protected resource it wants to access; the resulting authorization must be restricted and validated for the correct destination. Resource Indicators, RFC 8707
Illustrative scopes might be sessions:read, sessions:message, and sessions:control. These names are design examples, not standardized MCP scopes. Start with the smallest useful permission set and apply more granular local policy after token validation. Never forward a bridge access token as though it were an unrelated downstream service's token. MCP's security guidance explicitly treats token passthrough and confused-deputy behavior as risks. MCP security best practices
4 Admit the correct project and sessions
Discovery should return only admitted sessions. A broadly readable local filesystem is not a reason to expose every conversation on a computer.
Atlas Workspace's admission record should identify the permitted repository and supported worktrees. Avoid deciding identity solely from a friendly project name or a non-null project field. Worktrees, renamed directories, and sessions without a project identifier need explicit handling through verified native metadata.
A session reference should be stable and exact. Before sending anything, resolve the intended session, read its current goal, and check current ownership. If there are two similar “Analytics” sessions, do not select whichever one appears first.
Keep native session IDs separate from cloud task IDs and conversation callback identities. A local session may not have a corresponding cloud-thread object. Trying to look it up through the wrong registry can make a healthy session appear missing.
5 Expose a small workflow API
MCP tools have names and structured input schemas. That structure lets the client discover capabilities and construct bounded requests; it does not remove the need to validate input or enforce permissions inside every handler. MCP tools specification
The observed bridge contract separates discovery, reads, sends, goals, ownership, and recovery. A generalized version includes:
workflow_sessions: list admitted sessions with pagination.workflow_read: inspect state, turns, items, or approvals with bounded output.workflow_operation: retrieve the durable outcome of one exact operation.workflow_send: submit an authorized instruction in an explicitly supported mode.workflow_steerandworkflow_pause: target an exact active turn only when ownership permits.workflow_goal: manage a goal under explicit hash and policy preconditions.workflow_handover: perform supported writer transitions, not implied takeover.workflow_recover: reconcile uncertain owned work using authoritative evidence.
These are custom application tools, not commands supplied automatically by MCP.
For example, a read might request a specific thread's recent items with a small limit. Its response should include timestamps, pagination state, and enough identifiers to locate the relevant turn. A control request additionally needs a stable operation key, the exact target, and required goal or policy preconditions.
Reject malformed input early. Limit instruction size, validate enumerations, disallow arbitrary shell execution through generic workflow fields, and return errors that distinguish invalid input, unavailable resources, denied authority, stale state, and unknown outcomes.
6 Make operations durable before making them powerful
The hardest failure is not a clean rejection. It is: “The request timed out, but the session may already have received it.”
A recommended ledger records:
- Stable operation identity and request digest.
- Authenticated principal and admitted target.
- Requested action and applicable authorization reference.
- Expected goal, policy, route, or binding revision.
- Current state and timestamps.
- Native turn or receipt evidence when available.
- Terminal result or explicit recovery requirement.
Use a transactional store suited to the deployment. Database choice is less important than atomic admission and durable state transitions. A process-local dictionary is insufficient if a restart can erase whether an instruction was dispatched.
An illustrative state model is received → validated → accepted → dispatched → acknowledged → completed, with separate rejected, failed, and unknown outcomes. This is a conceptual model, not an exact schema copied from the deployed bridge.
Crucially, acceptance is not execution. Delivery is not completion. A model response is not proof that the requested feature works. Expose those distinctions to the assistant so it can report accurately.
Idempotency also has boundaries. The observed mailbox contract permits retrying an identical enqueue identity and payload after uncertain acceptance. The workflow execution contract instead directs callers to inspect the exact durable operation and never replay uncertain work. Do not generalize one endpoint's retry rule to another. An operation key is not a magical guarantee of exactly-once external effects.
7 Respect writer ownership and goal revisions
A local desktop and a remote bridge can both observe a session, but they should not silently become competing writers.
The observed workflow distinguishes bridge-owned execution from queued instructions addressed to an existing external owner. Queued acceptance does not mean immediate delivery, active-turn steering, or cancellation support. Conversely, bridge-owned steering requires the exact durable active turn and verified authority.
A proper handover is a protocol: establish eligible idle state, release ownership, observe the native transition, and reacquire only through the supported path. A handover is not an escape hatch for an uncertain prior operation.
Goals need similar protection. Store the objective and a hash or revision of its accepted contents. A control operation should fail if it expects an old goal but the user has edited the objective locally. Reconciliation must preserve the actual latest goal rather than overwrite it with stale bridge state.
A goal being active is not proof that a session is currently executing. A goal update is not a resume command. Completed or blocked goals should not be automatically reactivated simply because the connection returns.
8 Recover uncertainty without duplicate work
Suppose Atlas's workflow-editor session received a checkpoint instruction, but the bridge lost the acknowledgment. A later send encounters a recovery gate.
The safe sequence is:
- Query the original operation identity.
- Inspect its durable dispatch and receipt evidence.
- Search the correct native session and turn history within explicit bounds.
- Match the exact instruction or receipt, not merely similar words.
- Reconcile only when the evidence supports a particular outcome.
- Otherwise retain
unknownand explain what is still missing.
One observed incident was resolved after deeper history inspection found an acknowledgment outside the initial bounded view. Another remained unresolved even after the history reader was repaired and a much larger scan completed. That contrast matters: repairing transport does not manufacture missing evidence.
Do not clear recovery state merely to make the interface look healthy. Do not replay an old operation under a new ID. If the product needs an explicit abandon or quarantine procedure, design and review it as its own authorized state transition. Do not substitute an unsupported “force clear.”
9 Handle large histories as an engineering requirement
Local sessions grow. A reader that works on a short test conversation can fail on a long-running project.
Observed failure modes included an oversized native response exceeding a frame limit, a reader dying while its transport still appeared open, and a cursor compatibility problem across runtime versions. Raising a single timeout would not address those distinct causes.
A robust adapter should:
- Bound output by bytes as well as item count.
- Page through long histories using supported cursor semantics.
- Detect repeated cursors, missing progress, and partial coverage.
- Track the exact scanned range and stopping reason.
- Distinguish end-of-history from a read failure.
- Surface an unhealthy reader even if the socket remains connected.
- Test compatibility with the installed native runtime.
Never claim “no acknowledgment exists” from a scan that stopped at a frame error or truncated page. Say that the inspected range did not establish it.
10 Bind notifications to verified destinations
Reading session results and pushing notifications are different capabilities. Event delivery requires a verified subscriber and an authorized destination, not a chat identifier supplied inside untrusted metadata.
For Atlas Workspace, the bridge should associate subscriptions with the authenticated principal and the exact approved conversation route. A callback URL, session title, or claimed user name must not confer access by itself.
Treat acknowledgments as records of an exact receipt observed by the bound party. Acknowledging a receipt must not resend the command. Likewise, an event saying “deployment succeeded” should remain evidence to inspect, not an instruction to perform another deployment.
11 Test in layers before production use
Start with a disposable project session and harmless instructions. A useful test sequence is:
Connection tests: valid connection, expired token, wrong audience, denied scope, revoked grant, malformed metadata, and redirect mismatch.
Admission tests: allowed repository, unrelated repository, worktree, null project identifier, ambiguous session names, and stale session references.
Control tests: read-only monitoring, queued acceptance without execution, owned send, exact-turn steering, denied ownership, stale goal hash, and explicit handover boundaries.
Reliability tests: disconnect before dispatch, disconnect after dispatch, missing acknowledgment, duplicate enqueue, process restart, late acknowledgment, oversized history, cursor incompatibility, and unresolved recovery.
End-to-end proof: authorize one harmless instruction, verify the exact native receipt, observe its result, restart the bridge, and confirm the operation's durable outcome remains consistent without another effect.
Test names and counts are not the acceptance criteria. The important proof is that the intended action happened in the correct session, stayed within its authority, survived recovery checks, and did not affect unrelated work.
12 Deploy and verify the running service
Separate source readiness from runtime truth. A patch can pass tests while an older executable remains selected. A new release can be installed while the service still runs the previous process.
For an illustrative deployment, package a pinned release, retain the prior artifact, use a service supervisor, and keep credentials outside the source tree. Record the selected release, actual running build identity, service start time, configuration fingerprint, and health observations.
After deployment, perform a read-only session probe and a disposable end-to-end operation. Then verify restart persistence and a bounded recovery scenario. Do not test a bridge repair by disturbing important active project sessions.
A useful health report separates network reachability, OAuth validity, adapter health, session readability, writer eligibility, and recovery state. “Service running” proves only one part of that chain.
13 Report what the evidence actually proves
The bridge should help the dot explain progress, not inflate confidence.
- “Connected” means authenticated access succeeded.
- “Queued” means accepted for possible later delivery.
- “Acknowledged” means the exact receipt was observed.
- “Running” should be grounded in fresh native or process evidence.
- “Tested” is narrower than deployed.
- “Feature complete” requires the feature's acceptance criteria.
One particularly important lesson: an interrupted conversation-turn label did not prove that background replay jobs had stopped. Later checkpoints showed those jobs had continued. Status monitoring must correlate labels with recent artifacts, native messages, and process evidence instead of treating a single field as the whole truth.
An ordered build checklist
Implement the bridge in this order so every added capability has a tested foundation:
- Prove the local adapter first. Against a disposable session, read the exact session ID, current goal, recent items, and native runtime version. Confirm repository admission and pagination without exposing a remote writer.
- Add the protected endpoint and OAuth connection. Configure the registered client flow, publish discovery metadata, validate issuer, token audience, expiry, and scopes, and test rejected credentials. Complete connection through the supported client UI, not by pasting secrets into a conversation.
- Expose read tools. Implement discovery, bounded state/history reads, and approval inspection. Check that unrelated sessions remain invisible and that every result carries usable provenance and freshness information.
- Build the durable ledger. Before enabling sends, persist operation identity, payload digest, preconditions, state transitions, and native evidence. Test process crashes at each dispatch boundary and implement exact-operation lookup.
- Add one writer path at a time. Start with an authorized harmless instruction. Then implement ownership checks, goal revision guards, queued delivery semantics, and supported pause or steer behavior. Keep unsupported transitions explicit rather than approximating them.
- Implement recovery and negative tests. Exercise late or missing receipts, large histories, stale bindings, disconnected owners, and retries. Demonstrate that uncertainty blocks duplicate effects instead of disappearing after restart.
- Deploy and verify the real service. Pin the build, retain rollback artifacts, verify the running identity, repeat disposable end-to-end tests, and check that unrelated sessions are untouched. Document which capabilities are enabled and which remain read-only or unsupported.
The first useful milestone is a trustworthy read-only status request. The second is one authorized instruction whose exact receipt and outcome survive a restart. Expand only after those two paths are demonstrably reliable.
The practical takeaway
The OAuth connection is the entry door, not the entire bridge. The complete system combines authenticated access, narrow project admission, explicit action authority, native-session ownership, durable operation identities, exact acknowledgments, bounded observation, and conservative recovery.
Build read-only visibility first. Add controls only after their permissions and failure behavior are explicit. Preserve uncertainty when evidence is missing. Verify the running release, not just the code. Most importantly, never confuse permission to connect with permission to act.
Implementation guide for Atlas Workspace
This implementation appendix turns the architecture above into a concrete starting point. Atlas Workspace is fictional. The code below is an original illustrative scaffold, not a copy of a deployed service. The OAuth resource server and a bounded read tool are implemented as a module; the native runtime adapter, admission database, writer, and deployment integration remain explicit engineering work. Do not connect it to valuable sessions until the test matrix passes.
The examples intentionally start read only. A service that can authenticate and read safely is a useful milestone. A service that can start, pause, resume, or steer work needs the durable delivery and ownership machinery described later. Adding a write tool before those controls exist creates a misleading shortcut.
Versions and scope
For this appendix, npm package versions were checked on October 22, 2026: @modelcontextprotocol/server 2.3.1 and jose 6.2.13. The sample uses the SDK v2 package layout, not v1 imports under @modelcontextprotocol/sdk. Pin versions and commit a lockfile; review updates deliberately rather than installing an unbounded latest version in production. The official SDK package guide explains the split between the server package and optional runtime adapters.
A minimal development setup is:
1mkdir atlas-bridge2cd atlas-bridge3npm init -y4npm pkg set type=module5npm install --save-exact @modelcontextprotocol/server@2.3.1 jose@6.2.13 zod@4.6.76npm install --save-dev --save-exact typescript@5.9.3 @types/node@24.19.2
These commands create a new example project; they do not install a native session runtime, create an OAuth grant, or expose a public endpoint. Dependency versions identify the validation environment, not a permanent recommendation. Reassess advisories and compatibility when implementing this guide.
Use this TypeScript configuration for the module:
1{2 "compilerOptions": {3 "target": "ES2022",4 "module": "NodeNext",5 "moduleResolution": "NodeNext",6 "strict": true,7 "skipLibCheck": true,8 "outDir": "dist"9 },10 "include": ["bridge.ts"]11}
Establish the native boundary first
An adapter is not an undocumented endpoint
The sample declares a NativeAdapter interface. Its methods are our application interface, not a claim that a particular desktop application exposes similarly named HTTP routes. Implement the adapter only using an officially supported local API, SDK, extension interface, or authenticated relay available in your environment.
Before building OAuth, prove that the adapter can identify one harmless local session, read a bounded page, detect a closed connection, and report an accurate observation timestamp. Record the supported runtime version and capability set. If there is no supported control API, stop at read-only integration or choose a supported execution environment. Do not replace the adapter with arbitrary shell evaluation, direct edits to runtime databases, or filesystem scraping presented as authoritative session state.
Keep the public bridge and native runtime as separate trust domains. A public request must not select a socket path, executable, repository directory, or arbitrary upstream URL. Resolve the opaque session ID through a server-maintained admission map. That map should bind the authenticated principal to an approved repository identity, session identity, allowed actions, and policy baseline. A session title is a label, not authorization.
The adapter must also enforce aggregate byte limits, not just item counts. Fifty messages can still contain a huge tool result. Reject or truncate individual content safely, return a continuation cursor where supported, and distinguish a complete page from a partial observation. A timeout signal is useful only if the implementation actually propagates cancellation to its native transport.
Configure OAuth discovery
Separate the identity provider from the bridge
Use a dedicated authorization server. The bridge verifies access tokens but does not need to collect passwords, mint refresh tokens, or implement a login screen. Register the actual connector client with the provider using the registration mechanism both support. Configure the exact redirect URI displayed by that client; do not guess it from a tutorial. An interactive authorization-code flow should use PKCE, appropriate state protection, and the resource identifier for this bridge. Persistent authorization requires the account owner’s consent.
For the example resource https://bridge.example.com/mcp, serve this public discovery document at https://bridge.example.com/.well-known/oauth-protected-resource/mcp:
1{2 "resource": "https://bridge.example.com/mcp",3 "authorization_servers": ["https://identity.example.com"],4 "scopes_supported": ["sessions:read"],5 "bearer_methods_supported": ["header"]6}
An unauthenticated MCP request should receive a challenge pointing to that document. It should not receive an HTML sign-in form or a redirect loop. The resource metadata names the identity provider; the provider’s own metadata supplies authorization and token endpoints. The resource URL and access-token audience must agree. See OAuth Protected Resource Metadata and MCP authorization.
Do not advertise write scopes until write tools and their enforcement exist. Later, separate scopes such as sessions:send and sessions:control can narrow access. Scope possession is still only one gate: each operation also needs session admission, current user intent, native ownership, and execution-policy checks.
Implement the authenticated read server
Token profile and identity assumptions
This example deliberately accepts a narrow token profile: signed JWT access tokens with typ equal to at+jwt, RS256 signatures, the configured issuer and audience, expiration, a subject, and a client_id. It is inspired by the JWT access-token profile, but is not a full conformance suite. If your provider uses another supported access-token format, adapt the verifier to its documented profile. Opaque tokens require an appropriate introspection implementation, not a JWT parser. Never substitute an ID token for an API access token.
The user identity is the pair of issuer and subject. The OAuth client ID identifies the calling application, not necessarily the user. The code carries both. The admission layer must map that verified identity to local authorization; it must not trust a user ID supplied as a tool argument.
The maintained jose APIs validate signatures and claims and resolve signing keys from the configured JWKS endpoint. The endpoint is trusted configuration, not a URL taken from an incoming token. See the official jwtVerify documentation and createRemoteJWKSet documentation.
The bridge module
Save the following as bridge.ts. It exports a web-standard request handler. A host-specific entrypoint must mount it behind HTTPS, enforce request-size limits, and supply the real adapter and admission implementation. It intentionally contains no start-session, shell, or control tool.
1import { createMcpHandler, McpServer, requireBearerAuth,2 OAuthError, OAuthErrorCode, type AuthInfo } from '@modelcontextprotocol/server';3import { createRemoteJWKSet, jwtVerify, errors } from 'jose';4import * as z from 'zod/v4';56export const ReadInput = z.object({7 sessionId: z.string().min(1).max(200),8 cursor: z.string().max(2048).optional(),9 limit: z.number().int().min(1).max(50).default(20)10}).strict();11export const ReadOutput = z.object({12 sessionId: z.string(),13 observedAt: z.string(),14 items: z.array(z.object({ id: z.string(), text: z.string().max(8000) })).max(50),15 nextCursor: z.string().nullable(),16 truncated: z.boolean()17});18export type Principal = { issuer: string; subject: string; clientId: string };19export interface NativeAdapter {20 // Implement only against your runtime's supported interface.21 read(input: z.infer<typeof ReadInput>, signal: AbortSignal):22 Promise<z.infer<typeof ReadOutput>>;23}24export interface Admission {25 requireRead(principal: Principal, sessionId: string): Promise<void>;26}27export type Config = {28 resource: string; issuer: string; jwksUrl: string;29 allowedClientIds: Set<string>; allowedOrigins: Set<string>;30};3132export function makeEndpoint(cfg: Config, adapter: NativeAdapter, acl: Admission) {33 const resource = new URL(cfg.resource);34 const metadataUrl = new URL('/.well-known/oauth-protected-resource/mcp', resource);35 if (resource.protocol !== 'https:' || resource.pathname !== '/mcp' ||36 resource.search || resource.hash || resource.username || resource.password) {37 throw new Error('Configure an HTTPS resource ending in /mcp');38 }39 for (const url of [cfg.issuer, cfg.jwksUrl]) {40 if (new URL(url).protocol !== 'https:') throw new Error('HTTPS required');41 }42 const keys = createRemoteJWKSet(new URL(cfg.jwksUrl));43 const invalid = () => new OAuthError(OAuthErrorCode.InvalidToken, 'Token rejected');44 const verifier = {45 async verifyAccessToken(token: string): Promise<AuthInfo> {46 let result;47 try {48 result = await jwtVerify(token, keys, {49 issuer: cfg.issuer, audience: cfg.resource,50 algorithms: ['RS256'], typ: 'at+jwt',51 requiredClaims: ['exp', 'iat', 'sub', 'client_id'],52 clockTolerance: 553 });54 } catch (error) {55 // Infrastructure failures remain server errors, not a request to reconnect.56 if (error instanceof errors.JWTExpired ||57 error instanceof errors.JWTClaimValidationFailed ||58 error instanceof errors.JWSSignatureVerificationFailed ||59 error instanceof errors.JWSInvalid ||60 error instanceof errors.JWTInvalid ||61 error instanceof errors.JOSEAlgNotAllowed ||62 error instanceof errors.JWKSNoMatchingKey) throw invalid();63 throw error;64 }65 const p = result.payload;66 if (typeof p.sub !== 'string' || !p.sub || typeof p.exp !== 'number' ||67 typeof p.client_id !== 'string' || !cfg.allowedClientIds.has(p.client_id) ||68 typeof p.scope !== 'string') throw invalid();69 return {70 token, clientId: p.client_id, scopes: p.scope.split(/\s+/).filter(Boolean),71 expiresAt: p.exp, resource,72 extra: { issuer: cfg.issuer, subject: p.sub }73 };74 }75 };76 const gate = requireBearerAuth({ verifier, requiredScopes: ['sessions:read'],77 expectedResource: resource, resourceMetadataUrl: metadataUrl.href });7879 const mcp = createMcpHandler(({ authInfo }) => {80 const subject = authInfo?.extra?.subject;81 const issuer = authInfo?.extra?.issuer;82 if (!authInfo || typeof subject !== 'string' || typeof issuer !== 'string') {83 throw new Error('Missing verified principal');84 }85 const principal = { subject, issuer, clientId: authInfo.clientId };86 const server = new McpServer({ name: 'atlas-workspace-bridge', version: '0.1.0' });87 server.registerTool('sessions_read', {88 description: 'Read one admitted session with bounded pagination',89 inputSchema: ReadInput, outputSchema: ReadOutput,90 annotations: { readOnlyHint: true, destructiveHint: false }91 }, async (input) => {92 await acl.requireRead(principal, input.sessionId);93 const data = ReadOutput.parse(await adapter.read(input, AbortSignal.timeout(10_000)));94 if (data.sessionId !== input.sessionId) throw new Error('Adapter target mismatch');95 return { structuredContent: data,96 content: [{ type: 'text', text: JSON.stringify(data) }] };97 });98 return server;99 });100101 return {102 close: () => mcp.close(),103 async fetch(request: Request): Promise<Response> {104 const url = new URL(request.url);105 // The trusted host adapter must retain the external URL and reject bad Host headers.106 if (url.origin !== resource.origin) return new Response('Invalid host', { status: 403 });107 const origin = request.headers.get('origin');108 if (origin !== null && !cfg.allowedOrigins.has(origin)) {109 return new Response('Invalid origin', { status: 403 });110 }111 if (url.pathname === metadataUrl.pathname && request.method === 'GET') {112 return Response.json({ resource: cfg.resource,113 authorization_servers: [cfg.issuer], scopes_supported: ['sessions:read'],114 bearer_methods_supported: ['header'] });115 }116 if (url.pathname !== '/mcp') return new Response('Not found', { status: 404 });117 const authInfo = await gate(request);118 if (authInfo instanceof Response) return authInfo;119 return mcp.fetch(request, { authInfo });120 }121 };122}
The SDK’s createMcpHandler creates a fresh server instance for each request while shared dependencies remain outside the factory. requireBearerAuth produces authentication challenges and passes verified AuthInfo to that factory. These are SDK features, not a custom reinterpretation of MCP. The official HTTP handler API and bearer authentication API describe the exact interfaces used here.
This module does not implement CORS preflight, proxy normalization, TLS termination, request-body ceilings, rate limiting, audit persistence, or graceful host shutdown. Add those at the host boundary. If using Node, use a supported SDK runtime adapter rather than mixing a Fetch Request with a Node IncomingMessage; the official HTTP serving guide shows the supported adapters. Preserve the verified public origin across the proxy boundary without trusting arbitrary forwarded headers. A request URL check is not a replacement for validating the original Host header at ingress.
Do not log AuthInfo, because it contains the raw token. Log an internal principal identifier, request correlation ID, action, target, latency, and sanitized outcome instead. A JWKS outage should appear as an infrastructure problem; repeatedly asking the user to reconnect cannot repair a network failure. Local signature verification also does not provide instantaneous revocation: choose short lifetimes or provider-supported revocation/introspection controls according to risk.
Define the write contract before implementing it
Example schemas
A write request needs more than a session ID and a message. The following application schemas are a design proposal, not additional tools implemented by the read-only module:
1const SendInput = z.object({2 operationKey: z.string().uuid(),3 sessionId: z.string().min(1).max(200),4 expectedGoalHash: z.string().regex(/^[a-f0-9]{64}$/),5 message: z.string().min(1).max(16000),6 mode: z.enum(['owned', 'queued']),7 timeoutSeconds: z.number().int().min(1).max(900).optional(),8 acknowledgeDelayedDelivery: z.boolean().optional()9}).strict().superRefine((v, ctx) => {10 if (v.mode === 'owned' && v.timeoutSeconds === undefined)11 ctx.addIssue({code:'custom', message:'Owned execution needs a bounded lease'});12 if (v.mode === 'queued' &&13 (v.acknowledgeDelayedDelivery !== true || v.timeoutSeconds !== undefined))14 ctx.addIssue({code:'custom', message:'Queued delivery needs explicit acknowledgement and no lease'});15});1617const OperationOutput = z.object({18 operationKey: z.string().uuid(),19 sessionId: z.string(),20 state: z.enum(['accepted','dispatching','delivered','unknown','rejected','cancelled']),21 receiptId: z.string().nullable(),22 observedAt: z.string(),23 reasonCode: z.string().nullable()24});
Register separate tools for enqueueing, exact-operation lookup, and active-turn control. Keep tool descriptions precise about side effects. A read-only annotation is metadata for clients, not an authorization mechanism. Verify scopes and admission inside the execution path even if the UI hides unavailable tools.
The example intentionally omits indefinite execution. Add continuous ownership only if the runtime supports it and the user explicitly authorizes it. Likewise, queued must not secretly acquire an active session. Queue acceptance means “stored for later consideration,” not “delivered” or “running.”
Build a durable operation ledger
PostgreSQL schema
Use a transactional database, not an in-memory map, for operation identity and recovery. This illustrative SQL separates operation records, session authority, and append-only evidence. Database roles, migrations, retention, backups, encryption, and access policies still need implementation.
1CREATE TABLE bridge_operations (2 tenant_id text NOT NULL,3 principal_id text NOT NULL,4 operation_key uuid NOT NULL,5 session_id text NOT NULL,6 action text NOT NULL,7 payload_sha256 char(64) NOT NULL,8 payload jsonb NOT NULL,9 state text NOT NULL CHECK (state IN10 ('accepted','dispatching','delivered','unknown','rejected','cancelled')),11 expected_goal_hash char(64) NOT NULL,12 native_receipt_id text,13 dispatch_generation bigint,14 reason_code text,15 created_at timestamptz NOT NULL DEFAULT now(),16 updated_at timestamptz NOT NULL DEFAULT now(),17 PRIMARY KEY (tenant_id, principal_id, operation_key)18);1920CREATE UNIQUE INDEX bridge_receipt_unique21 ON bridge_operations (tenant_id, native_receipt_id)22 WHERE native_receipt_id IS NOT NULL;2324CREATE UNIQUE INDEX bridge_one_uncertain_dispatch25 ON bridge_operations (tenant_id, session_id)26 WHERE state IN ('dispatching','unknown');2728CREATE TABLE bridge_session_authority (29 tenant_id text NOT NULL,30 session_id text NOT NULL,31 owner_id text,32 generation bigint NOT NULL DEFAULT 0,33 lease_until timestamptz,34 goal_hash char(64) NOT NULL,35 policy_hash char(64) NOT NULL,36 active_turn_id text,37 recovery_required boolean NOT NULL DEFAULT false,38 PRIMARY KEY (tenant_id, session_id)39);4041CREATE TABLE bridge_operation_events (42 event_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,43 tenant_id text NOT NULL,44 principal_id text NOT NULL,45 operation_key uuid NOT NULL,46 event_type text NOT NULL,47 evidence jsonb NOT NULL,48 created_at timestamptz NOT NULL DEFAULT now(),49 FOREIGN KEY (tenant_id, principal_id, operation_key)50 REFERENCES bridge_operations (tenant_id, principal_id, operation_key)51);
The native receipt uniqueness constraint assumes the adapter supplies identifiers unique within a tenant. If the runtime guarantees uniqueness only within a session, include session ID in that constraint. Define this before shipping, rather than discovering collisions during recovery.
Atomic acceptance and duplicate detection
For a new request, validate and authorize first. Canonicalize the typed payload with a deterministic representation, including action, target, message, goal precondition, and execution mode. Hash that representation. Do not hash arbitrary JSON serialization whose object-key order can vary. Store only the fields required to deliver and reconcile the operation; never store bearer tokens in the payload.
Inside one transaction, insert the operation using ON CONFLICT DO NOTHING, then read the existing or newly inserted row and compare both its digest and canonical payload within that same transaction. If both match, return the existing state. If they differ, return an explicit key-conflict error without changing the stored operation. The exact-operation read must use the authenticated principal’s namespace, not a caller-supplied tenant. PostgreSQL’s INSERT documentation defines the conflict behavior; the cross-request idempotency policy is application logic.
The API must never turn a duplicate unknown operation into another dispatch. Idempotent acceptance of the same mailbox entry and idempotent execution by a native runtime are different properties. A ledger guarantees that the bridge recognizes a duplicate; it does not magically make an external side effect transactional.
Dispatch and crash recovery
Implement the worker as a state machine:
- Lock the relevant session-authority row and select one eligible accepted operation.
- Recheck admission, current permissions, goal hash, policy hash, native ownership, and whether another turn is active. Delayed work must not inherit stale authority.
- Allocate a generation and bounded lease. Persist
dispatchingand its event before calling the adapter. - Dispatch with the operation key and fencing generation only if the native interface supports their documented semantics.
- On a verified receipt, record
delivered, the native receipt, and evidence in one transaction. Delivery is not goal completion. - If rejection proves that nothing executed, record
rejected. If the process, connection, or deadline fails after dispatch might have occurred, recordunknownand gate further conflicting work. - Reconcile unknown work through read-only receipt/status/history lookup. Never infer nonexecution merely because a recent history page contains no receipt.
Workers may use SELECT … FOR UPDATE SKIP LOCKED to avoid contending for the same queue row, but must still serialize per-session authority. PostgreSQL documents locking and SKIP LOCKED; skipping a row is not a complete queue correctness algorithm.
There is an unavoidable crash window between an external native effect and committing its receipt. Without downstream deduplication or an authoritative reconciliation API, exactly-once execution cannot be promised. On restart, unfinished dispatch records become recovery candidates, not automatically retryable jobs. Lease expiry also does not prove that a previously dispatched native turn stopped.
Preserve ownership and goals
Fencing and safe handoffs
Use the database generation as a fencing token only if the downstream runtime actually rejects obsolete generations. A counter stored solely in the bridge cannot prevent a stalled old worker from later issuing a native command. Without downstream fencing, restrict concurrent writers, reconcile before takeover, and keep ambiguous operations blocked.
A pause or steer operation must address the exact active turn the bridge owns. It must not mean “find whatever turn is active now and interrupt it.” A handoff to desktop control should require a verified safe state, preserve goal identity and accounting, and explicitly release bridge authority. Reacquisition should verify that the goal and policy have not changed during the handoff.
Keep native goal state separate from a textual instruction. Editing a goal should use a compare-and-swap precondition such as the current goal hash and should not silently resume paused work. Completed or blocked goals must not automatically reactivate merely because a transport reconnects. These are application invariants to test against the actual runtime, not behavior supplied by OAuth.
Separate permissions for enqueueing, controlling a turn, changing goals, and changing execution policy. The last of these can materially expand authority and should not be hidden inside a convenience retry. A successful login permits the authorized API operation; it does not remove the user’s approval requirements for what that operation ultimately does.
Configure and deploy without leaking credentials
Nonsecret configuration
An example configuration file can contain public identifiers and operating limits:
1BRIDGE_PUBLIC_RESOURCE=https://bridge.example.com/mcp2OAUTH_ISSUER=https://identity.example.com3OAUTH_JWKS_URL=https://identity.example.com/jwks4OAUTH_ALLOWED_CLIENT_IDS=atlas-example-client5BRIDGE_BIND_ADDRESS=127.0.0.16BRIDGE_PORT=30007BRIDGE_WRITES_ENABLED=false8BRIDGE_READ_TIMEOUT_MS=100009BRIDGE_MAX_REQUEST_BYTES=26214410BRIDGE_MAX_RESPONSE_BYTES=52428811BRIDGE_LOG_LEVEL=info
These names are proposed application settings; the module above does not automatically read them. Your entrypoint must validate and map them to its configuration. Wire every advertised limit to enforcement and test it. Keep database credentials, local relay credentials, and any provider secrets in the deployment’s approved secret facility. Never put them in the article, a repository example, process arguments, screenshots, or chat messages.
Supervisor example
The following is a Linux systemd illustration, not a claim about the original deployment. The paths are fictional. entrypoint.js must be implemented to load configuration, initialize the adapter/database, mount the handler, and shut down cleanly.
1[Unit]2Description=Atlas Workspace Bridge3After=network-online.target4Wants=network-online.target56[Service]7Type=simple8User=atlas-bridge9WorkingDirectory=/opt/atlas-bridge/current10EnvironmentFile=/etc/atlas-bridge/config.env11ExecStart=/usr/bin/node /opt/atlas-bridge/current/dist/entrypoint.js12Restart=on-failure13RestartSec=514TimeoutStopSec=3015NoNewPrivileges=true16PrivateTmp=true17ProtectSystem=strict18ReadWritePaths=/var/lib/atlas-bridge1920[Install]21WantedBy=multi-user.target
A supervisor restarts a process; it cannot decide that an uncertain operation is safe to replay. On termination, stop accepting new writes, drain safe work, persist uncertainty, and close transports. On startup, inspect unfinished ledger records before enabling writes. Check permissions against the real adapter: an overly broad service account is unsafe, while an overly restrictive sandbox can prevent the intended local socket access. See the official systemd service documentation.
Reverse proxy example
Inside an already configured HTTPS server block for bridge.example.com, a starting NGINX location is:
1location / {2 client_max_body_size 256k;3 proxy_pass http://127.0.0.1:3000;4 proxy_http_version 1.1;5 proxy_set_header Host bridge.example.com;6 proxy_set_header X-Forwarded-Proto https;7 proxy_set_header Connection "";8 proxy_buffering off;9 proxy_cache off;10 proxy_read_timeout 60s;11 proxy_next_upstream off;12}
Complete TLS provisioning, certificate renewal, default-host rejection, trusted-proxy rules, and access-log redaction separately. Do not expose the loopback service directly. Disabling buffering helps streaming responses, but the timeout is not a native execution lease. Choose stream keepalives and proxy deadlines together. Automatic upstream retries are disabled here to avoid obscuring delivery uncertainty; use explicit ledger-aware recovery instead. The NGINX proxy module documentation explains these directives.
Put the pieces together in order
Setup sequence
- Inventory supported capabilities. Identify the actual native transport, runtime version, session admission mechanism, receipt lookup, and ownership controls. Mark unsupported functions explicitly.
- Implement the adapter against a fixture. Read one disposable session. Test pagination, byte ceilings, timeout propagation, reconnect, and wrong-target rejection.
- Implement principal admission. Map verified issuer/subject pairs to allowed sessions and actions. Include a negative test proving that one user cannot read another user’s session.
- Configure the identity provider. Register the connector, exact callback, resource, and read scope through the approved consent process. Confirm its access-token profile and signing-key lifecycle.
- Mount metadata and the read-only endpoint. Start locally behind the intended host adapter. Verify challenge headers before trying the connector UI.
- Deploy a read-only canary. Confirm the externally selected release, actual running process, build identifier, and a real authenticated bounded read. A green unit test or a running PID alone is insufficient.
- Add the database ledger. Apply reviewed migrations and test concurrent duplicate requests, payload conflicts, retention, recovery after restart, and backup restoration.
- Implement queue and ownership controls. Use disposable sessions to test stale goals, native owner changes, expired leases, delayed delivery, and unknown outcomes.
- Enable a single bounded write. Use an explicitly approved harmless message, a new operation key, and exact receipt verification. Do not use valuable ongoing work as the first integration test.
- Expand gradually. Add control tools and notifications only after their authorization and recovery tests pass. Keep a read-only switch and rollback plan, with database compatibility documented.
Record evidence for each step: request correlation ID, version, timestamp, sanitized result, and the assertion it proves. Avoid a single “bridge healthy” badge that conflates identity-provider availability, MCP transport, adapter connectivity, database durability, native ownership, and goal progress.
Test failures as carefully as success
Acceptance matrix
Test | Required result |
|---|---|
No access token | 401 challenge with the correct resource metadata URL; no adapter call |
Expired token or invalid signature | Rejected; no session information disclosed |
Wrong issuer or audience | Rejected even if the signature is cryptographically valid |
ID token presented as access token | Rejected by the configured token profile |
Valid token without read scope | 403 insufficient scope; no adapter call |
Correct scope but unadmitted session | Access denied without cross-user data leakage |
Forged Origin or Host | Rejected before reaching the native adapter |
Oversized request or response | Bounded failure or explicit truncation; service remains responsive |
Duplicate operation and identical payload | Existing durable result returned; no second dispatch |
Duplicate key with changed message | Explicit conflict; original operation unchanged |
Native side effect followed by lost response | Unknown recovery state; no blind retry |
Crash after dispatch but before receipt commit | Restart gates the session and reconciles the original operation |
Stale goal or policy hash | No dispatch; explicit precondition failure |
Native ownership changes while queued | Eligibility rechecked; no unauthorized takeover |
Old worker resumes after lease expiry | Downstream fence rejects it, or deployment fails closed |
Late receipt outside first history page | Bounded deeper lookup finds it, or result stays explicitly incomplete |
Invalid or obsolete pagination cursor | Typed error; no silent empty result interpreted as completion |
Failed frame reader with open socket | Adapter reports unhealthy and closes pending reads deterministically |
Pause requested for a different active turn | Rejected; unrelated work remains untouched |
Service restart after successful delivery | Receipt survives; accepted work is not repeated |
Use fault injection for the ambiguous cases. Disconnect the adapter at controlled points, kill a test worker between state transitions, and run two workers against the same session. Test stale native callbacks and delayed ACKs, not just ordinary request timeouts. Verify the absence of unintended effects, not merely the presence of an error message.
Troubleshooting by layer
Symptom | Inspect first | Safe response |
|---|---|---|
Connector says to connect with OAuth | Grant state, metadata discovery, exact callback, issuer/audience | Repair authorization without replaying old operations |
Login succeeds but tools fail | Scope enforcement, host routing, protocol compatibility | Verify a harmless read and inspect sanitized error codes |
Tool accepts a message but session does not respond | Ledger state, queue eligibility, native receipt, ownership | Report accepted versus delivered accurately |
Reads hang after a large result | Frame decoder, reader liveness, byte limits | Repair transport and reconnect; do not reset the goal |
Recent history has no ACK | Cursor version, page boundary, truncation flags | Search bounded additional evidence; absence is not proof |
A session is reported interrupted | Native current state and fresh progress evidence | Distinguish a stale label from a real blocker |
Release was deployed but behavior is unchanged | Selected release, running executable, build ID | Verify the service actually runs the intended build |
A database lease expired | Native active turn and dispatch evidence | Do not treat expiry as proof of safe takeover |
A signing-key fetch fails | Provider/JWKS availability, DNS, egress | Diagnose infrastructure rather than repeatedly reconnecting |
Validation performed
The exact bridge.ts module shown above was type-checked with TypeScript 5.9.3 on Node 24.19.0 against the pinned dependencies. The following local checks also passed. The first group uses a rejecting fake adapter and verifies metadata, several rejection paths, and schema behavior. A second group uses an ephemeral locally generated signing key, a mocked JWKS response, and an admitted fake session. It verifies rejection of a correctly signed token without the required scope and successful authenticated MCP tool execution with the correct scope. Neither group contacts a real identity provider or native session.
Save this as checks.mjs beside bridge.ts:
1import assert from 'node:assert/strict';2import { makeEndpoint, ReadInput, ReadOutput } from './dist/bridge.js';3let reads = 0;4const endpoint = makeEndpoint({ resource: 'https://bridge.example.com/mcp',5 issuer: 'https://identity.example.com', jwksUrl: 'https://identity.example.com/jwks',6 allowedClientIds: new Set(['atlas-example-client']), allowedOrigins: new Set() },7 { read: async () => { reads++; throw new Error('Unexpected adapter access'); } },8 { requireRead: async () => { throw new Error('Unexpected ACL access'); } });9const meta = await endpoint.fetch(new Request('https://bridge.example.com/.well-known/oauth-protected-resource/mcp'));10assert.equal(meta.status, 200);11assert.equal((await meta.json()).resource, 'https://bridge.example.com/mcp');12const missing = await endpoint.fetch(new Request('https://bridge.example.com/mcp'));13assert.equal(missing.status, 401);14assert.match(missing.headers.get('www-authenticate'), /resource_metadata/);15const malformed = await endpoint.fetch(new Request('https://bridge.example.com/mcp', {headers:{authorization:'Bearer invalid'}}));16assert.equal(malformed.status, 401);17assert.equal((await endpoint.fetch(new Request('https://wrong.example.com/mcp'))).status,403);18assert.equal((await endpoint.fetch(new Request('https://bridge.example.com/mcp',{headers:{origin:'https://wrong.example.com'}}))).status,403);19assert.equal(ReadInput.parse({sessionId:'fixture'}).limit,20);20assert.equal(ReadInput.safeParse({sessionId:'fixture',limit:51}).success,false);21assert.equal(ReadInput.safeParse({sessionId:'fixture',extra:'bad'}).success,false);22assert.equal(ReadOutput.safeParse({sessionId:'fixture',observedAt:'now',items:[],nextCursor:null,truncated:false}).success,true);23assert.equal(reads,0);24await endpoint.close();25console.log('PASS: metadata, missing token, malformed token, host, origin, input bounds, unknown fields, output shape, no adapter access');
Run the checks after installing the pinned dependencies:
1./node_modules/.bin/tsc --noEmit2./node_modules/.bin/tsc3node checks.mjs
A full connector authorization flow, live-provider key rotation and revocation, native reads, SQL migrations, worker fault injection, and the supervisor/proxy configurations were not executed as part of this article. Treat those as required integration work, not implied successes from the checks above. The successful signature test establishes the scaffold’s local JWT-to-tool path, not the behavior of an external provider or deployment.
What remains before production
The appendix supplies the resource-server shape, typed read boundary, write-contract proposal, ledger schema, and operational checklist. It does not supply a complete identity-provider setup, approved native adapter, database access layer, transactional worker, downstream fencing, complete authorization policy, CORS policy, browser callback flow, event subscriber verification, or deployment-specific secret management.
Production acceptance therefore requires independent review and integration tests in the actual environment. Keep proof of authenticated access separate from proof of safe control. Keep proof of delivery separate from proof that the session completed its goal. When evidence is incomplete, an explicit unknown state is a correct result, not something to hide with an optimistic retry.

