Ctrl K
ComunidadGuías…

app/scripts/smoke-mcp.mjs

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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)
}