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';
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. It states that artifacts are 'uniquely identified by a slug-based ID with UUID suffix', which adds some context about identification. However, it lacks critical details: it doesn't mention if this is a write operation (implied by 'publish'), what permissions are needed, whether it's idempotent, or what happens on failure. For a mutation tool with zero annotation coverage, this is insufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences, front-loaded with the core purpose ('Publish a new artifact to your TOYBOX') and followed by a clarifying detail about identification. Every sentence adds value without redundancy, making it appropriately sized and efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (2 parameters with nested objects, no output schema, and no annotations), the description is incomplete. It lacks information on return values, error handling, authentication needs, or side effects. For a mutation tool with significant input structure, this leaves too many gaps for effective agent use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 50%, and the description adds no parameter-specific information beyond what the schema provides. It mentions 'slug-based ID with UUID suffix', which relates to the 'slug' parameter in metadata, but this is already covered in the schema description. Since schema coverage is moderate, the description doesn't compensate enough to elevate the score above baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('publish') and resource ('artifact to your TOYBOX'), specifying it creates something new. It distinguishes from siblings like 'list_artifacts' by focusing on creation rather than listing. However, it doesn't explicitly differentiate from 'initialize_toybox' or 'setup_remote' in terms of when to use each, keeping it from a perfect score.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives like 'initialize_toybox' or 'update_config'. It mentions that artifacts are uniquely identified, but this is more about behavior than usage context. Without explicit when/when-not instructions or named alternatives, it offers minimal usage guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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