Skip to main content
Glama

publish_artifact

Publish React components, SVGs, or Mermaid diagrams to your GitHub Pages portfolio. Upload artifacts with metadata including title, slug, type, and tags for organized storage and display.

Instructions

Publish a new artifact to your TOYBOX. Artifacts are uniquely identified by a slug-based ID with UUID suffix.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
codeYesThe React component code for the artifact (do not include React import - uses new JSX transform)
metadataYes

Implementation Reference

  • The main handler function `publishArtifact` that executes the tool logic: gets active repo, validates artifact code, generates ID and file content, saves/updates artifact, commits and pushes changes to GitHub, returns success/error with URL.
    export async function publishArtifact(params: PublishArtifactParams): Promise<PublishResult> {
      const { code, metadata } = params;
      const configService = new ConfigService();
    
      log.info('Starting artifact publication', { title: metadata.title, type: metadata.type });
    
      try {
        // Step 1: Get active TOYBOX repository from config
        log.debug('Looking for active TOYBOX repository...');
        log.info('Looking for active TOYBOX repository');
        const activeRepo = await configService.getActiveRepository();
        
        if (!activeRepo) {
          log.error('No active TOYBOX repository found');
          return {
            success: false,
            artifactId: '',
            artifactUrl: '',
            error: 'No active TOYBOX repository found. Please run initialize_toybox first or set an active repository.',
          };
        }
    
        const localPath = activeRepo.localPath;
        log.info('Using TOYBOX repository', { localPath, repoName: activeRepo.name });
        log.info('Using TOYBOX at path', { localPath });
        
        // Update last used timestamp
        await configService.touchRepository(activeRepo.name);
    
        // Step 2: Initialize services
        const gitService = new GitService(localPath);
        const artifactService = new ArtifactService(localPath);
    
        // Step 3: Validate artifact code
        log.debug('Validating artifact code...');
        log.info('Validating artifact code');
        const validation = artifactService.validateArtifactCode(code);
        if (!validation.valid) {
          log.error('Code validation failed', { issues: validation.issues });
          return {
            success: false,
            artifactId: '',
            artifactUrl: '',
            error: `Code validation failed: ${validation.issues.join(', ')}`,
          };
        }
    
        // Step 4: Generate artifact ID and check for conflicts
        const artifactId = artifactService.generateArtifactId(metadata.slug);
        log.info('Generated artifact ID', { artifactId, slug: metadata.slug, title: metadata.title });
        log.info('Generated artifact ID', { artifactId });
    
        // Step 5: Generate artifact file content
        const artifactContent = artifactService.generateArtifactFile(code, metadata);
    
        // Step 6: Pull latest changes to avoid conflicts
        log.debug('Pulling latest changes...');
        log.info('Pulling latest changes');
        try {
          await gitService.pull();
          log.debug('Successfully pulled latest changes');
        } catch (error) {
          log.warn('Could not pull changes, proceeding with local state', { error: error instanceof Error ? error.message : String(error) });
          log.warn('Could not pull changes, proceeding with local state');
        }
    
        // Step 7: Save artifact to filesystem
        log.debug('Saving artifact...');
        log.info('Saving artifact');
        let filePath: string;
        try {
          filePath = await artifactService.saveArtifact(artifactId, artifactContent);
          log.info('Artifact saved successfully', { filePath });
        } catch (error) {
          // If artifact exists, try updating it instead
          if (error instanceof Error && error.message.includes('already exists')) {
            log.info('Artifact exists, updating...', { artifactId });
            log.info('Artifact exists, updating');
            filePath = await artifactService.updateArtifact(artifactId, artifactContent);
            log.info('Artifact updated successfully', { filePath });
          } else {
            throw error;
          }
        }
    
        // Step 8: Stage changes
        log.debug('Staging changes...');
        log.info('Staging changes');
        const relativePath = path.relative(localPath, filePath);
        await gitService.addFiles([relativePath]);
        log.debug('Files staged', { stagedFiles: [relativePath] });
    
        // Step 9: Check if there are changes to commit
        const hasChanges = await gitService.hasUncommittedChanges();
        if (!hasChanges) {
          log.warn('No changes detected, artifact may already be up to date');
          return {
            success: false,
            artifactId,
            artifactUrl: '',
            error: 'No changes detected. Artifact may already be up to date.',
          };
        }
    
        // Step 10: Commit changes
        const commitMessages = [
          `feat: Add/update artifact "${metadata.title}"`,
          `- Type: ${metadata.type}
    - Tags: ${metadata.tags?.join(', ') || 'none'}${metadata.description ? `\n- Description: ${metadata.description}` : ''}
    - Created: ${metadata.createdAt}
    - Updated: ${metadata.updatedAt}`
        ];
    
        log.debug('Committing changes...', { commitMessages });
        log.info('Committing changes');
        await gitService.commit(commitMessages);
        log.info('Changes committed successfully');
    
        // Step 11: Push to GitHub
        log.debug('Pushing to GitHub...');
        log.info('Pushing to GitHub');
        await gitService.push();
        log.info('Changes pushed to GitHub successfully');
    
        // Step 12: Get published URL
        const baseUrl = activeRepo.publishedUrl || `https://example.github.io/${activeRepo.name}`;
        const artifactUrl = artifactService.generateArtifactUrl(artifactId, baseUrl);
        
        log.info('Artifact published successfully', { 
          artifactId, 
          artifactUrl, 
          baseUrl,
          title: metadata.title 
        });
    
        return {
          success: true,
          artifactId,
          artifactUrl,
          message: `✅ Artifact published successfully!\n\n` +
                   `Title: ${metadata.title}\n` +
                   `ID: ${artifactId}\n` +
                   `URL: ${artifactUrl}\n` +
                   `Gallery: ${baseUrl}\n\n` +
                   `Your artifact is now live! It may take a few minutes for GitHub Pages to update.`,
        };
    
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        log.error('Failed to publish artifact', { error: errorMessage, title: metadata.title });
        
        return {
          success: false,
          artifactId: '',
          artifactUrl: '',
          error: `Failed to publish artifact: ${errorMessage}`,
        };
      }
    }
  • Zod schema `PublishArtifactParamsSchema` defining input validation for the tool (code and metadata), plus related `PublishArtifactParams` type.
    export const PublishArtifactParamsSchema = z.object({
      code: z.string(),
      metadata: ArtifactMetadataSchema,
    });
    
    export type PublishArtifactParams = z.infer<typeof PublishArtifactParamsSchema>;
  • src/index.ts:128-158 (registration)
    Tool registration in `ListToolsRequestSchema` handler: defines `publish_artifact` tool name, description, and inputSchema (JSON schema mirroring the Zod schema).
    {
      name: 'publish_artifact',
      description: 'Publish a new artifact to your TOYBOX. Artifacts are uniquely identified by a slug-based ID with UUID suffix.',
      inputSchema: {
        type: 'object',
        properties: {
          code: {
            type: 'string',
            description: 'The React component code for the artifact (do not include React import - uses new JSX transform)',
          },
          metadata: {
            type: 'object',
            properties: {
              title: { type: 'string', description: 'Display title for the artifact (can include any characters, emojis, etc.)' },
              slug: { 
                type: 'string', 
                description: 'URL-friendly identifier in lowercase kebab-case (e.g., "my-component", "todo-app"). Only lowercase letters, numbers, and hyphens allowed.',
              },
              description: { type: 'string', description: 'Optional description' },
              type: { type: 'string', enum: ['react', 'svg', 'mermaid'], description: 'Artifact type' },
              tags: { type: 'array', items: { type: 'string' }, description: 'Optional tags' },
              folder: { type: 'string', description: 'Optional folder grouping' },
              createdAt: { type: 'string', description: 'ISO date string' },
              updatedAt: { type: 'string', description: 'ISO date string' },
            },
            required: ['title', 'slug', 'type', 'createdAt', 'updatedAt'],
          },
        },
        required: ['code', 'metadata'],
      },
    },
  • src/index.ts:236-249 (registration)
    Tool execution routing in `CallToolRequestSchema` handler: switch case for 'publish_artifact' that parses args with schema and calls `publishArtifact` handler.
    case 'publish_artifact': {
      const params = PublishArtifactParamsSchema.parse(args);
      log.info('Executing publish_artifact', { title: params.metadata.title });
      const result = await publishArtifact(params);
      log.info('publish_artifact completed', { result });
      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify(result, null, 2),
          },
        ],
      };
    }
  • src/index.ts:16-16 (registration)
    Import statement for the `publishArtifact` handler from './handlers/publish.js'.
    import { publishArtifact } from './handlers/publish.js';

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/isnbh0/toybox-mcp-server'

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