Skip to main content
Glama
replit-client.ts22.7 kB
import fetch from 'node-fetch'; export interface ReplitConfig { token: string; replId?: string; } export interface FileInfo { path: string; type: 'file' | 'directory'; } export interface ReplInfo { id: string; slug: string; title: string; description: string; language: string; url: string; } interface MutationResult { __typename: string; message?: string; } const GRAPHQL_URL = 'https://replit.com/graphql'; export class ReplitClient { private token: string; private replId?: string; constructor(config: ReplitConfig) { this.token = config.token; this.replId = config.replId; } private async query(query: string, variables: Record<string, unknown> = {}): Promise<unknown> { const response = await fetch(GRAPHQL_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Cookie': `connect.sid=${this.token}`, 'X-Requested-With': 'XMLHttpRequest', 'User-Agent': 'Replit-MCP-Server/1.0', }, body: JSON.stringify({ query, variables }), }); if (!response.ok) { throw new Error(`GraphQL request failed: ${response.status} ${response.statusText}`); } const result = await response.json() as { data?: unknown; errors?: Array<{ message: string }> }; if (result.errors && result.errors.length > 0) { throw new Error(`GraphQL error: ${result.errors.map(e => e.message).join(', ')}`); } return result.data; } private checkMutationResult(result: MutationResult | null, successType: string, operation: string): void { if (!result) { throw new Error(`${operation} failed: No response from server`); } if (result.__typename !== successType) { throw new Error(`${operation} failed: ${result.message || 'Unknown error'}`); } } setReplId(replId: string): void { this.replId = replId; } async getCurrentUser(): Promise<{ id: string; username: string; email: string } | null> { const query = ` query CurrentUser { currentUser { id username email } } `; const data = await this.query(query) as { currentUser: { id: string; username: string; email: string } | null }; return data.currentUser; } async getReplByUrl(url: string): Promise<ReplInfo | null> { const query = ` query ReplByUrl($url: String!) { repl(url: $url) { id slug title description templateInfo { label } url } } `; const data = await this.query(query, { url }) as { repl: { id: string; slug: string; title: string; description: string; templateInfo?: { label: string }; url: string } | null }; if (!data.repl) return null; return { id: data.repl.id, slug: data.repl.slug, title: data.repl.title, description: data.repl.description, language: data.repl.templateInfo?.label || 'unknown', url: data.repl.url, }; } async listUserRepls(limit: number = 20): Promise<ReplInfo[]> { const query = ` query UserRepls($limit: Int!) { currentUser { repls(count: $limit) { items { id slug title description templateInfo { label } url } } } } `; const data = await this.query(query, { limit }) as { currentUser: { repls: { items: Array<{ id: string; slug: string; title: string; description: string; templateInfo?: { label: string }; url: string }> } } | null }; if (!data.currentUser) { throw new Error('Failed to get user repls: User not authenticated or no access'); } return data.currentUser.repls.items.map(repl => ({ id: repl.id, slug: repl.slug, title: repl.title, description: repl.description, language: repl.templateInfo?.label || 'unknown', url: repl.url, })); } async readFile(path: string, replId?: string): Promise<string> { const targetReplId = replId || this.replId; if (!targetReplId) { throw new Error('No repl ID specified. Please provide a replId or set a default repl.'); } const query = ` query ReadFile($replId: String!, $path: String!) { repl(id: $replId) { ... on Repl { fileByPath(path: $path) { ... on File { content } } } } } `; const data = await this.query(query, { replId: targetReplId, path }) as { repl: { fileByPath: { content: string } | null } | null }; if (!data.repl) { throw new Error(`Repl not found or no access: ${targetReplId}`); } if (!data.repl.fileByPath) { throw new Error(`File not found: ${path}`); } return data.repl.fileByPath.content; } async writeFile(path: string, content: string, replId?: string): Promise<boolean> { const targetReplId = replId || this.replId; if (!targetReplId) { throw new Error('No repl ID specified. Please provide a replId or set a default repl.'); } const query = ` mutation WriteFile($replId: String!, $path: String!, $content: String!) { writeFile(replId: $replId, path: $path, content: $content) { __typename ... on WriteFileError { message } } } `; const data = await this.query(query, { replId: targetReplId, path, content }) as { writeFile: MutationResult | null }; this.checkMutationResult(data.writeFile, 'WriteFileSuccess', `Write file "${path}"`); return true; } async listFiles(path: string = '.', replId?: string): Promise<FileInfo[]> { const targetReplId = replId || this.replId; if (!targetReplId) { throw new Error('No repl ID specified. Please provide a replId or set a default repl.'); } const query = ` query ListFiles($replId: String!, $path: String!) { repl(id: $replId) { ... on Repl { fileByPath(path: $path) { ... on Directory { children { filename ... on File { __typename } ... on Directory { __typename } } } } } } } `; const data = await this.query(query, { replId: targetReplId, path }) as { repl: { fileByPath: { children: Array<{ filename: string; __typename: string }> } | null } | null }; if (!data.repl) { throw new Error(`Repl not found or no access: ${targetReplId}`); } if (!data.repl.fileByPath) { throw new Error(`Directory not found: ${path}`); } return data.repl.fileByPath.children.map(child => ({ path: child.filename, type: child.__typename === 'Directory' ? 'directory' : 'file', })); } async createFile(path: string, content: string = '', replId?: string): Promise<boolean> { return this.writeFile(path, content, replId); } async deleteFile(path: string, replId?: string): Promise<boolean> { const targetReplId = replId || this.replId; if (!targetReplId) { throw new Error('No repl ID specified. Please provide a replId or set a default repl.'); } const query = ` mutation DeleteFile($replId: String!, $path: String!) { deleteFile(replId: $replId, path: $path) { __typename ... on DeleteFileError { message } } } `; const data = await this.query(query, { replId: targetReplId, path }) as { deleteFile: MutationResult | null }; this.checkMutationResult(data.deleteFile, 'DeleteFileSuccess', `Delete file "${path}"`); return true; } async createDirectory(path: string, replId?: string): Promise<boolean> { const targetReplId = replId || this.replId; if (!targetReplId) { throw new Error('No repl ID specified. Please provide a replId or set a default repl.'); } const query = ` mutation CreateDirectory($replId: String!, $path: String!) { createDirectory(replId: $replId, path: $path) { __typename ... on CreateDirectoryError { message } } } `; const data = await this.query(query, { replId: targetReplId, path }) as { createDirectory: MutationResult | null }; this.checkMutationResult(data.createDirectory, 'CreateDirectorySuccess', `Create directory "${path}"`); return true; } async runRepl(replId?: string): Promise<{ success: boolean; message: string }> { const targetReplId = replId || this.replId; if (!targetReplId) { throw new Error('No repl ID specified. Please provide a replId or set a default repl.'); } const query = ` mutation RunRepl($replId: String!) { runRepl(replId: $replId) { __typename ... on RunReplError { message } } } `; const data = await this.query(query, { replId: targetReplId }) as { runRepl: MutationResult | null }; if (!data.runRepl) { return { success: false, message: 'No response from server' }; } if (data.runRepl.__typename !== 'RunReplSuccess') { return { success: false, message: data.runRepl.message || 'Failed to start repl' }; } return { success: true, message: 'Repl started successfully' }; } async stopRepl(replId?: string): Promise<{ success: boolean; message: string }> { const targetReplId = replId || this.replId; if (!targetReplId) { throw new Error('No repl ID specified. Please provide a replId or set a default repl.'); } const query = ` mutation StopRepl($replId: String!) { stopRepl(replId: $replId) { __typename ... on StopReplError { message } } } `; const data = await this.query(query, { replId: targetReplId }) as { stopRepl: MutationResult | null }; if (!data.stopRepl) { return { success: false, message: 'No response from server' }; } if (data.stopRepl.__typename !== 'StopReplSuccess') { return { success: false, message: data.stopRepl.message || 'Failed to stop repl' }; } return { success: true, message: 'Repl stopped successfully' }; } async searchFiles(query_str: string, replId?: string): Promise<Array<{ path: string; matches: string[] }>> { const targetReplId = replId || this.replId; if (!targetReplId) { throw new Error('No repl ID specified. Please provide a replId or set a default repl.'); } const gqlQuery = ` query SearchFiles($replId: String!, $query: String!) { repl(id: $replId) { ... on Repl { search(query: $query) { results { path matches } } } } } `; const data = await this.query(gqlQuery, { replId: targetReplId, query: query_str }) as { repl: { search: { results: Array<{ path: string; matches: string[] }> } | null } | null }; if (!data.repl) { throw new Error(`Repl not found or no access: ${targetReplId}`); } if (!data.repl.search) { return []; } return data.repl.search.results; } async getUserById(id: number): Promise<{ id: string; username: string; bio: string; isHacker: boolean; timeCreated: string } | null> { const query = ` query UserById($id: Int!) { user(id: $id) { id username bio isHacker timeCreated } } `; const data = await this.query(query, { id }) as { user: { id: string; username: string; bio: string; isHacker: boolean; timeCreated: string } | null }; return data.user; } async getUserByUsername(username: string): Promise<{ id: string; username: string; bio: string; isHacker: boolean; timeCreated: string } | null> { const query = ` query UserByUsername($username: String!) { userByUsername(username: $username) { id username bio isHacker timeCreated } } `; const data = await this.query(query, { username }) as { userByUsername: { id: string; username: string; bio: string; isHacker: boolean; timeCreated: string } | null }; return data.userByUsername; } async createRepl(input: { title: string; language: string; description?: string; isPrivate?: boolean }): Promise<ReplInfo> { const query = ` mutation CreateRepl($input: CreateReplInput!) { createRepl(input: $input) { __typename ... on Repl { id slug title description templateInfo { label } url } ... on UserError { message } } } `; const data = await this.query(query, { input }) as { createRepl: { __typename: string; id?: string; slug?: string; title?: string; description?: string; templateInfo?: { label: string }; url?: string; message?: string } }; if (data.createRepl.__typename === 'UserError') { throw new Error(`Create repl failed: ${data.createRepl.message}`); } return { id: data.createRepl.id!, slug: data.createRepl.slug!, title: data.createRepl.title!, description: data.createRepl.description || '', language: data.createRepl.templateInfo?.label || 'unknown', url: data.createRepl.url!, }; } async forkRepl(url: string): Promise<ReplInfo> { const query = ` mutation ForkRepl($url: String!) { forkRepl(url: $url) { __typename ... on Repl { id slug title description templateInfo { label } url } ... on UserError { message } } } `; const data = await this.query(query, { url }) as { forkRepl: { __typename: string; id?: string; slug?: string; title?: string; description?: string; templateInfo?: { label: string }; url?: string; message?: string } }; if (data.forkRepl.__typename === 'UserError') { throw new Error(`Fork repl failed: ${data.forkRepl.message}`); } return { id: data.forkRepl.id!, slug: data.forkRepl.slug!, title: data.forkRepl.title!, description: data.forkRepl.description || '', language: data.forkRepl.templateInfo?.label || 'unknown', url: data.forkRepl.url!, }; } async deleteRepl(id: string, confirm: boolean): Promise<{ success: boolean; message: string }> { if (!confirm) { throw new Error('Delete repl requires confirmation. Set confirm to true to proceed.'); } const query = ` mutation DeleteRepl($id: String!) { deleteRepl(id: $id) { id } } `; const data = await this.query(query, { id }) as { deleteRepl: { id: string } | null }; if (!data.deleteRepl) { throw new Error('Delete repl failed: No response from server'); } return { success: true, message: `Repl ${id} deleted successfully` }; } async getSecrets(replId?: string): Promise<Array<{ key: string; value: string }>> { const targetReplId = replId || this.replId; if (!targetReplId) { throw new Error('No repl ID specified. Please provide a replId or set a default repl.'); } const query = ` query GetSecrets($replId: String!) { repl(id: $replId) { ... on Repl { environmentVariables { key value } } } } `; const data = await this.query(query, { replId: targetReplId }) as { repl: { environmentVariables: Array<{ key: string; value: string }> } | null }; if (!data.repl) { throw new Error(`Repl not found or no access: ${targetReplId}`); } return data.repl.environmentVariables || []; } async setSecret(key: string, value: string, replId?: string): Promise<{ key: string; value: string }> { const targetReplId = replId || this.replId; if (!targetReplId) { throw new Error('No repl ID specified. Please provide a replId or set a default repl.'); } const query = ` mutation SetEnvironmentVariable($replId: String!, $key: String!, $value: String!) { setEnvironmentVariable(replId: $replId, key: $key, value: $value) { key value } } `; const data = await this.query(query, { replId: targetReplId, key, value }) as { setEnvironmentVariable: { key: string; value: string } | null }; if (!data.setEnvironmentVariable) { throw new Error('Set environment variable failed: No response from server'); } return data.setEnvironmentVariable; } async deleteSecret(key: string, replId?: string): Promise<{ success: boolean; message: string }> { const targetReplId = replId || this.replId; if (!targetReplId) { throw new Error('No repl ID specified. Please provide a replId or set a default repl.'); } const query = ` mutation DeleteEnvironmentVariable($replId: String!, $key: String!) { deleteEnvironmentVariable(replId: $replId, key: $key) } `; await this.query(query, { replId: targetReplId, key }); return { success: true, message: `Environment variable "${key}" deleted successfully` }; } async getDeployment(replId?: string): Promise<{ id: string; status: string; url: string } | null> { const targetReplId = replId || this.replId; if (!targetReplId) { throw new Error('No repl ID specified. Please provide a replId or set a default repl.'); } const query = ` query GetDeployment($replId: String!) { repl(id: $replId) { ... on Repl { currentDeployment { id status url } } } } `; const data = await this.query(query, { replId: targetReplId }) as { repl: { currentDeployment: { id: string; status: string; url: string } | null } | null }; if (!data.repl) { throw new Error(`Repl not found or no access: ${targetReplId}`); } return data.repl.currentDeployment; } async createDeployment(replId?: string): Promise<{ success: boolean; message: string; deploymentId?: string }> { const targetReplId = replId || this.replId; if (!targetReplId) { throw new Error('No repl ID specified. Please provide a replId or set a default repl.'); } const query = ` mutation CreateDeployment($replId: String!) { deployRepl(replId: $replId) { __typename ... on DeployReplSuccess { deployment { id status url } } ... on UserError { message } } } `; const data = await this.query(query, { replId: targetReplId }) as { deployRepl: { __typename: string; deployment?: { id: string; status: string; url: string }; message?: string } }; if (data.deployRepl.__typename === 'UserError') { throw new Error(`Deploy repl failed: ${data.deployRepl.message}`); } return { success: true, message: 'Deployment initiated successfully', deploymentId: data.deployRepl.deployment?.id, }; } async getReplDetails(replId?: string, url?: string): Promise<{ id: string; slug: string; title: string; description: string; language: string; url: string; runCount: number; likeCount: number; forkCount: number; tags: string[]; isPrivate: boolean; multiplayers: Array<{ username: string }>; comments: Array<{ id: string; body: string; user: { username: string } }>; }> { let queryVar: Record<string, unknown> = {}; let replSelector = ''; let varDecl = ''; if (replId) { queryVar = { replId }; replSelector = 'repl(id: $replId)'; varDecl = '$replId: String!'; } else if (url) { queryVar = { url }; replSelector = 'repl(url: $url)'; varDecl = '$url: String!'; } else if (this.replId) { queryVar = { replId: this.replId }; replSelector = 'repl(id: $replId)'; varDecl = '$replId: String!'; } else { throw new Error('No repl ID or URL specified. Please provide a replId, url, or set a default repl.'); } const query = ` query GetReplDetails(${varDecl}) { ${replSelector} { ... on Repl { id slug title description templateInfo { label } url runCount likeCount forkCount tags isPrivate multiplayers { username } comments { items { id body user { username } } } } } } `; const data = await this.query(query, queryVar) as { repl: { id: string; slug: string; title: string; description: string; templateInfo?: { label: string }; url: string; runCount: number; likeCount: number; forkCount: number; tags: string[]; isPrivate: boolean; multiplayers: Array<{ username: string }>; comments: { items: Array<{ id: string; body: string; user: { username: string } }> }; } | null }; if (!data.repl) { throw new Error('Repl not found or no access'); } return { id: data.repl.id, slug: data.repl.slug, title: data.repl.title, description: data.repl.description, language: data.repl.templateInfo?.label || 'unknown', url: data.repl.url, runCount: data.repl.runCount, likeCount: data.repl.likeCount, forkCount: data.repl.forkCount, tags: data.repl.tags || [], isPrivate: data.repl.isPrivate, multiplayers: data.repl.multiplayers || [], comments: data.repl.comments?.items || [], }; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/NOVA-3951/Replit-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server