Skip to main content
Glama
discourse

Discourse MCP

Official
by discourse

Get Draft

discourse_get_draft

Retrieve saved draft content from Discourse forums using specific keys like 'new_topic' for new topics or 'topic_' for replies. Access partially composed posts to continue editing where you left off.

Instructions

Retrieve a specific draft by its key. Common keys: "new_topic" for new topic drafts, "topic_" for reply drafts.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
draft_keyYesDraft key (e.g., "new_topic", "topic_123", "new_private_message")
sequenceNoExpected sequence number (optional)

Implementation Reference

  • The handler function for the discourse_get_draft tool. It fetches the draft from the Discourse API using the provided draft_key and optional sequence, parses the JSON data, formats a markdown response with preview and full JSON, and handles errors.
    async (input: unknown, _extra: unknown) => {
      const { draft_key, sequence } = schema.parse(input);
    
      try {
        const { client } = ctx.siteState.ensureSelectedSite();
        const params = new URLSearchParams();
        if (typeof sequence === "number") params.set("sequence", String(sequence));
    
        const url = `/drafts/${encodeURIComponent(draft_key)}.json${params.toString() ? `?${params}` : ""}`;
        const data = (await client.get(url)) as {
          draft?: string;
          draft_sequence?: number;
        };
    
        if (!data?.draft) {
          return { content: [{ type: "text", text: `No draft found for key "${draft_key}".` }] };
        }
    
        let parsedDraft: Record<string, unknown> = {};
        try {
          parsedDraft = JSON.parse(data.draft);
        } catch {
          parsedDraft = { raw: data.draft };
        }
    
        const lines = [`# Draft: \`${draft_key}\`\n`];
        lines.push(`**Sequence:** ${data.draft_sequence ?? "unknown"}`);
    
        if (parsedDraft.title) lines.push(`**Title:** ${parsedDraft.title}`);
        if (parsedDraft.categoryId) lines.push(`**Category ID:** ${parsedDraft.categoryId}`);
        if (parsedDraft.tags && Array.isArray(parsedDraft.tags)) {
          lines.push(`**Tags:** ${(parsedDraft.tags as string[]).join(", ")}`);
        }
        if (parsedDraft.action) lines.push(`**Action:** ${parsedDraft.action}`);
    
        if (parsedDraft.reply) {
          lines.push(`\n**Content:**\n${parsedDraft.reply}`);
        }
    
        lines.push("\n```json");
        lines.push(
          JSON.stringify(
            {
              draft_key,
              draft_sequence: data.draft_sequence,
              data: parsedDraft,
            },
            null,
            2
          )
        );
        lines.push("```");
    
        return { content: [{ type: "text", text: lines.join("\n") }] };
      } catch (e: unknown) {
        const msg = e instanceof Error ? e.message : String(e);
        return { content: [{ type: "text", text: `Failed to get draft: ${msg}` }], isError: true };
      }
    }
  • Zod input schema defining draft_key (required string) and optional sequence (number). Used for validation in the tool handler.
    const schema = z.object({
      draft_key: z
        .string()
        .min(1)
        .max(40)
        .describe('Draft key (e.g., "new_topic", "topic_123", "new_private_message")'),
      sequence: z.number().int().min(0).optional().describe("Expected sequence number (optional)"),
    });
  • Registration of the discourse_get_draft tool using server.registerTool, including name, metadata (title, description, inputSchema), and handler function.
    server.registerTool(
      "discourse_get_draft",
      {
        title: "Get Draft",
        description:
          'Retrieve a specific draft by its key. Common keys: "new_topic" for new topic drafts, "topic_<id>" for reply drafts.',
        inputSchema: schema.shape,
      },
      async (input: unknown, _extra: unknown) => {
        const { draft_key, sequence } = schema.parse(input);
    
        try {
          const { client } = ctx.siteState.ensureSelectedSite();
          const params = new URLSearchParams();
          if (typeof sequence === "number") params.set("sequence", String(sequence));
    
          const url = `/drafts/${encodeURIComponent(draft_key)}.json${params.toString() ? `?${params}` : ""}`;
          const data = (await client.get(url)) as {
            draft?: string;
            draft_sequence?: number;
          };
    
          if (!data?.draft) {
            return { content: [{ type: "text", text: `No draft found for key "${draft_key}".` }] };
          }
    
          let parsedDraft: Record<string, unknown> = {};
          try {
            parsedDraft = JSON.parse(data.draft);
          } catch {
            parsedDraft = { raw: data.draft };
          }
    
          const lines = [`# Draft: \`${draft_key}\`\n`];
          lines.push(`**Sequence:** ${data.draft_sequence ?? "unknown"}`);
    
          if (parsedDraft.title) lines.push(`**Title:** ${parsedDraft.title}`);
          if (parsedDraft.categoryId) lines.push(`**Category ID:** ${parsedDraft.categoryId}`);
          if (parsedDraft.tags && Array.isArray(parsedDraft.tags)) {
            lines.push(`**Tags:** ${(parsedDraft.tags as string[]).join(", ")}`);
          }
          if (parsedDraft.action) lines.push(`**Action:** ${parsedDraft.action}`);
    
          if (parsedDraft.reply) {
            lines.push(`\n**Content:**\n${parsedDraft.reply}`);
          }
    
          lines.push("\n```json");
          lines.push(
            JSON.stringify(
              {
                draft_key,
                draft_sequence: data.draft_sequence,
                data: parsedDraft,
              },
              null,
              2
            )
          );
          lines.push("```");
    
          return { content: [{ type: "text", text: lines.join("\n") }] };
        } catch (e: unknown) {
          const msg = e instanceof Error ? e.message : String(e);
          return { content: [{ type: "text", text: `Failed to get draft: ${msg}` }], isError: true };
        }
      }
    );
Behavior3/5

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

No annotations are provided, so the description carries the full burden. It discloses the tool's behavior as a retrieval operation, but lacks details on permissions, error handling, rate limits, or what happens if the draft key is invalid. The mention of 'sequence' as optional in the schema is not explained in the description, leaving gaps in behavioral context.

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 front-loaded with the core purpose in the first sentence, followed by helpful examples in the second sentence. Every sentence adds value without redundancy, making it efficient and well-structured for quick understanding.

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

Completeness3/5

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

Given the tool's low complexity (simple retrieval), no annotations, and no output schema, the description is adequate but incomplete. It covers the purpose and basic usage but lacks details on behavioral aspects like return format, error cases, or prerequisites, which are important for a tool without structured annotations or output schema.

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 100%, so the schema fully documents both parameters (draft_key and sequence). The description adds minimal value by providing examples of common keys ('new_topic', 'topic_<id>'), but does not explain parameter interactions or semantics beyond what the schema already covers, aligning with the baseline for high schema coverage.

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

Purpose5/5

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

The description clearly states the verb ('Retrieve') and resource ('a specific draft'), and distinguishes it from siblings by specifying retrieval by key rather than listing all drafts (like discourse_list_drafts). The examples of common keys ('new_topic', 'topic_<id>') further clarify the scope and differentiate it from other retrieval tools.

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

Usage Guidelines4/5

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

The description provides clear context for when to use this tool (to retrieve a specific draft by key) and implies an alternative (discourse_list_drafts for listing drafts). However, it does not explicitly state when not to use it or compare it to all sibling tools, such as discourse_get_user or discourse_read_post, which might handle different resources.

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/discourse/discourse-mcp'

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