app/scripts/smoke-mcp.mjs
Rama main · réplica de lectura del repositorio original.
import assert from 'node:assert/strict'
import { createHash, randomBytes, randomUUID } from 'node:crypto'
import { fileURLToPath } from 'node:url'
import { createSmokeRpc } from './smoke-rpc.mjs'
import { smokeRealtime } from './smoke-realtime.mjs'
import { makeSignature } from 'better-auth/crypto'
import { request as httpRequest } from 'node:http'
import {
Client,
StreamableHTTPClientTransport,
} from '@modelcontextprotocol/client'
// Undici fetch sets sec-fetch-mode:cors even when callers try to model a
// navigation. Native HTTP preserves browser navigation metadata for this probe.
function browserNavigation(url, headers) {
return new Promise((resolve, reject) => {
const request = httpRequest(
url,
{
headers: {
...headers,
accept: 'text/html',
'sec-fetch-mode': 'navigate',
'sec-fetch-dest': 'document',
},
},
(response) => {
const chunks = []
response.on('data', (chunk) => chunks.push(chunk))
response.on('error', reject)
response.on('end', () =>
resolve(
new Response(Buffer.concat(chunks), {
status: response.statusCode,
headers: response.headers,
}),
),
)
},
)
request.on('error', reject)
request.end()
})
}
// Receives the disposable store from smoke-worker. No live account, key or
// browser settings are touched, and generated credentials are never printed.
export async function smokeMcp({
origin,
query,
authSecret,
supportsCimd = false,
development = false,
modulePrefix = 'src',
appDir = fileURLToPath(new URL('../', import.meta.url)),
}) {
const userId = randomUUID(),
teamId = randomUUID(),
memberId = randomUUID()
const apiKeyId = randomUUID(),
sessionId = randomUUID()
const secret = `dp_${randomBytes(32).toString('hex')}`
const sessionToken = randomBytes(32).toString('hex')
const now = Date.now(),
expiry = now + 3_600_000
const scopes = JSON.stringify([
'projects:read',
'servers:read',
'deployments:read',
'backups:read',
])
await query(
0,
`insert into user(id,name,username,email) values('${userId}','Worker MCP user','${userId}','${userId}@example.invalid')`,
)
await query(
0,
`insert into teams(id,name,slug) values('${teamId}','Worker MCP team','${teamId}')`,
)
await query(
0,
`insert into team_members(id,user_id,team_id,role) values('${memberId}','${userId}','${teamId}','admin')`,
)
await query(
0,
`insert into session(id,token,user_id,expires_at,created_at,updated_at) values('${sessionId}','${sessionToken}','${userId}',${expiry},${now},${now})`,
)
await query(
0,
`insert into api_keys(id,user_id,team_id,membership_id,name,prefix,key_hash,role_ceiling,preset,project_ids,scopes,expires_at,created_at) values('${apiKeyId}','${userId}','${teamId}','${memberId}','Worker MCP key','${secret.slice(0, 11)}','${createHash('sha256').update(secret).digest('hex')}','admin','read',null,'${scopes}',${expiry},${now})`,
)
const metadata = await fetch(
`${origin}/.well-known/oauth-protected-resource/mcp`,
)
assert.equal(metadata.status, 200)
assert.equal((await metadata.json()).resource, `${origin}/mcp`)
const provider = await fetch(
`${origin}/.well-known/oauth-authorization-server/api/auth`,
)
assert.equal(provider.status, 200)
const providerMetadata = await provider.json()
assert.equal(providerMetadata.issuer, `${origin}/api/auth`)
assert.equal(
providerMetadata.client_id_metadata_document_supported,
supportsCimd ? true : undefined,
'CIMD metadata must match the runtime transport capability',
)
const apiIdentity = await fetch(`${origin}/api/v1/identity`, {
headers: { authorization: `Bearer ${secret}` },
})
assert.equal(apiIdentity.status, 200)
assert.equal((await apiIdentity.json()).user.id, userId)
async function sdkConnection(token) {
const client = new Client(
{ name: 'devpush-worker-smoke', version: '2.3.1' },
{ versionNegotiation: { mode: 'auto' } },
)
await client.connect(
new StreamableHTTPClientTransport(new URL(`${origin}/mcp`), {
requestInit: { headers: { authorization: `Bearer ${token}` } },
}),
)
assert.equal(client.getNegotiatedProtocolVersion(), '2026-07-28')
const tools = await client.listTools()
assert(tools.tools.some((tool) => tool.name === 'list_projects'))
assert(!tools.tools.some((tool) => tool.name === 'deploy'))
const identity = await client.callTool({
name: 'get_identity',
arguments: {},
})
assert.notEqual(identity.isError, true)
assert.equal(identity.structuredContent.user.id, userId)
await client.close()
}
await sdkConnection(secret)
const signature = await makeSignature(sessionToken, authSecret)
const browserHeaders = {
cookie: `devpush.session_token=${encodeURIComponent(sessionToken + '.' + signature)}`,
origin,
'content-type': 'application/json',
accept: 'application/json',
}
await smokeRealtime({
origin,
teamSlug: teamId,
cookie: browserHeaders.cookie,
})
const registered = await fetch(`${origin}/api/auth/oauth2/register`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
client_name: 'Worker MCP assistant',
redirect_uris: ['http://127.0.0.1:9021/callback'],
token_endpoint_auth_method: 'none',
grant_types: ['authorization_code', 'refresh_token'],
}),
})
assert(
registered.status >= 200 && registered.status < 300,
await registered.clone().text(),
)
const { client_id: clientId } = await registered.json()
assert.match(clientId, /^[a-zA-Z0-9]+$/)
const consentAction = await createSmokeRpc({
origin,
headers: browserHeaders,
appDir,
name: 'completeMcpConsentAction',
modulePath: `${modulePrefix}/server/mcp/functions.ts`,
development,
bundleDir: supportsCimd ? '.output/server/_ssr' : 'dist/server/assets',
})
async function authorizeConnection() {
const before = new Set(
(
await query(0, `select id from api_grants where user_id='${userId}'`)
).map((row) => row.id),
)
const verifier = randomBytes(32).toString('base64url')
const params = new URLSearchParams({
client_id: clientId,
redirect_uri: 'http://127.0.0.1:9022/callback',
response_type: 'code',
code_challenge_method: 'S256',
code_challenge: createHash('sha256').update(verifier).digest('base64url'),
scope: 'devpush:read offline_access',
resource: `${origin}/mcp`,
state: 'worker-mcp-state',
})
const authorization = await browserNavigation(
`${origin}/api/auth/oauth2/authorize?${params}`,
browserHeaders,
)
assert.equal(authorization.status, 302, await authorization.text())
const consentUrl = new URL(authorization.headers.get('location'), origin)
assert.equal(consentUrl.pathname, '/oauth/consent')
const wrappedPage = await browserNavigation(consentUrl, browserHeaders)
assert.equal(wrappedPage.status, 302)
const wrappedUrl = new URL(wrappedPage.headers.get('location'), origin)
assert.equal(wrappedUrl.origin, origin)
assert.equal(wrappedUrl.searchParams.size, 1)
assert.equal(
wrappedUrl.searchParams.get('oauthQuery'),
consentUrl.search.slice(1),
)
const consentPage = await browserNavigation(wrappedUrl, browserHeaders)
const consentHtml = await consentPage.text()
assert.equal(
consentPage.status,
200,
consentPage.headers.get('location') ?? consentHtml.slice(0, 2000),
)
assert.match(
consentHtml,
/Connect app|Connect|Authoriz|data-slot="skeleton"/,
)
const consent = await consentAction({
accepted: true,
teamId,
preset: 'read',
includeLogs: false,
oauthQuery: consentUrl.search.slice(1),
})
assert.ok(consent.result?.redirectUrl, JSON.stringify(consent.error))
const callback = new URL(consent.result.redirectUrl, origin)
const grants = await query(
0,
`select id from api_grants where user_id='${userId}'`,
)
const grantId = grants.find((row) => !before.has(row.id))?.id
assert.ok(grantId, 'Browser consent creates its own grant')
assert.equal(callback.searchParams.get('state'), 'worker-mcp-state')
assert.equal(callback.searchParams.get('iss'), `${origin}/api/auth`)
const exchange = await fetch(`${origin}/api/auth/oauth2/token`, {
method: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: clientId,
grant_type: 'authorization_code',
code: callback.searchParams.get('code'),
code_verifier: verifier,
redirect_uri: 'http://127.0.0.1:9022/callback',
resource: `${origin}/mcp`,
}),
})
assert.equal(exchange.status, 200, await exchange.clone().text())
const tokens = await exchange.json()
assert.equal(tokens.token_type, 'Bearer')
return { tokens, grantId }
}
const { tokens, grantId } = await authorizeConnection()
await sdkConnection(tokens.access_token)
await query(
0,
`update api_grants set revoked_at=${Date.now()} where id='${grantId}'`,
)
const denied = await fetch(`${origin}/mcp`, {
method: 'POST',
headers: {
authorization: `Bearer ${tokens.access_token}`,
'content-type': 'application/json',
accept: 'application/json, text/event-stream',
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'tools/list',
params: {},
}),
})
assert.equal(denied.status, 401)
const { tokens: replayTokens } = await authorizeConnection()
const refreshInput = new URLSearchParams({
client_id: clientId,
grant_type: 'refresh_token',
refresh_token: replayTokens.refresh_token,
resource: `${origin}/mcp`,
})
const refreshRequest = () =>
fetch(`${origin}/api/auth/oauth2/token`, {
method: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
body: refreshInput,
})
const refreshed = await refreshRequest()
assert.equal(refreshed.status, 200, await refreshed.clone().text())
const rotated = await refreshed.json()
await sdkConnection(rotated.access_token)
const replay = await refreshRequest()
assert(replay.status >= 400)
const replayDenied = await fetch(`${origin}/mcp`, {
method: 'POST',
headers: {
authorization: `Bearer ${rotated.access_token}`,
'content-type': 'application/json',
accept: 'application/json, text/event-stream',
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'tools/list',
params: {},
}),
})
assert.equal(replayDenied.status, 401)
}