Skip to content

src/index.ts

87 lines · 2.5 KB · TypeScript
Raw
import { kody } from 'kody:runtime'

const SERVER_NAME = 'playrunner'
const SERVER_URL = 'https://playrunner.cloud/mcp'

type ConnectResult = {
	name: string
	state: string
	toolCount: number
	authUrl: string | null
	alreadyPresent: boolean
	nextStep: string
}

/**
 * Connect this Kody account to Playrunner Cloud.
 * Call this once, before anything else in the package: it registers the
 * Playrunner MCP server if it is not already saved, and returns the
 * authorisation link when the account still needs to approve access.
 *
 * Playrunner supports dynamic client registration, so no API token or manual
 * configuration is needed — Kody registers itself and the user approves in the
 * browser.
 *
 * @param input - Optional overrides. `url` targets a self-hosted Playrunner.
 * @returns The saved server's state, its discovered tool count, and an
 * `authUrl` the user must open when authorisation is outstanding.
 *
 * @example
 * import connect from 'kody:@playrunner/mcp'
 *
 * const result = await connect()
 * if (result.authUrl) {
 *   // Ask the user to open result.authUrl, then call status().
 * }
 */
export default async function connect(
	input: { url?: string } = {},
): Promise<ConnectResult> {
	const url = input.url?.trim() || SERVER_URL
	const existing = await findServer()

	if (existing) {
		return {
			alreadyPresent: true,
			authUrl: existing.authUrl ?? null,
			name: existing.name,
			nextStep: describe(existing),
			state: existing.state,
			toolCount: existing.toolCount ?? 0,
		}
	}

	const added = await kody.mcpServerAdd({ name: SERVER_NAME, url })
	return {
		alreadyPresent: false,
		authUrl: added.authUrl ?? null,
		name: added.name,
		nextStep: describe(added),
		state: added.state,
		toolCount: added.toolCount ?? 0,
	}
}

type ServerRecord = {
	authUrl?: string | null
	name: string
	state: string
	toolCount?: number
	tools?: string[]
}

export async function findServer(): Promise<ServerRecord | null> {
	const listed = await kody.mcpServerList({})
	const servers: ServerRecord[] = listed.servers ?? []
	return servers.find((server) => server.name === SERVER_NAME) ?? null
}

export function describe(server: ServerRecord): string {
	if (server.authUrl) {
		return `Ask the user to open ${server.authUrl} and approve access, then call status().`
	}
	if (server.state === 'ready') {
		return `Connected. Call kody.mcp["${SERVER_NAME}"].list_workflows({}) to see what this account can run.`
	}
	return `Server is ${server.state}. Call kody.mcpServerReconnect({ server: "${SERVER_NAME}" }) if it stays that way.`
}