Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@dudko.dev/agent-web",
"version": "0.0.11",
"version": "0.0.12",
"description": "Headless, configurable in-browser LLM agent on the Vercel AI SDK: multi-provider bring-your-own-key (OpenAI, Anthropic, Google, xAI, DeepSeek, gateway, openai-compatible) AND local WebGPU/WebLLM, native + prompted tool-calling, structured output, an encrypted IndexedDB token vault, and a plan \u2192 execute \u2192 replan \u2192 synthesize loop. UI-agnostic.",
"type": "module",
"sideEffects": false,
Expand Down
5 changes: 5 additions & 0 deletions src/mcp/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
export { connectMcpHttp, flattenContent } from './http.js'
// Re-exported so a host can identify an authorization failure by identity.
// The SDK's class does not set `name`, so `err.name === 'UnauthorizedError'`
// is always false — string sniffing is not an option, and a host that only
// depends on this package has no other handle on the class.
export { UnauthorizedError } from '@modelcontextprotocol/sdk/client/auth.js'
export type {
McpHttpServerConfig,
McpCatalogEntry,
Expand Down
18 changes: 18 additions & 0 deletions tests/mcp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
flattenContent,
MemoryOAuthStorage,
readOAuthCallback,
UnauthorizedError,
VaultOAuthStorage,
} from '../dist/mcp.js'

Expand Down Expand Up @@ -849,3 +850,20 @@ test('OAuth: a dead refresh token falls back to a fresh authorization', async ()
// the same doomed refresh.
assert.equal(await provider.tokens(), undefined)
})

test('the subpath re-exports UnauthorizedError so a host can identify auth failures', async () => {
const mock = createMockServer({ requireAuth: true })
const provider = makeProvider()
const mcp = await connectMcpHttp({
docs: { url: MCP_URL, authProvider: provider, fetch: mock.fetchFn },
})
assert.equal(mcp.results[0].needsAuthorization, true)

// The SDK's class never assigns `this.name`, so it reads as "Error" —
// a host checking err.name would silently never match. Identity is the
// only reliable handle, and it has to be reachable from this package.
const err = new UnauthorizedError()
assert.equal(err.name, 'Error', 'guards the assumption this export exists for')
assert.ok(err instanceof UnauthorizedError)
assert.ok(err instanceof Error)
})
Loading