{
    "$schema": "https://static.modelcontextprotocol.io/schemas/mcp-server-card/v1.json",
    "version": "1.0",
    "protocolVersion": "2025-06-18",
    "serverInfo": {
        "name": "abcdef-studio",
        "title": "ABCDEF Studio",
        "version": "1d.179"
    },
    "description": "Blog content management: plan topics, draft and edit articles, add images, and approve, schedule and publish posts to WordPress, Shopify and custom sites, with search and analytics performance.",
    "iconUrl": "https://abcdef.studio/img/abcdef-icon-512.png",
    "documentationUrl": "https://abcdef.studio/agents.md",
    "websiteUrl": "https://abcdef.studio/mcp-info",
    "transport": {
        "type": "streamable-http",
        "endpoint": "https://abcdef.studio/mcp"
    },
    "capabilities": {
        "tools": {
            "listChanged": false
        }
    },
    "authentication": {
        "required": true,
        "schemes": [
            "oauth2",
            "bearer"
        ],
        "oauth2": {
            "protectedResourceMetadata": "https://abcdef.studio/oauth-metadata?doc=resource",
            "authorizationServerMetadata": "https://abcdef.studio/.well-known/openid-configuration",
            "scopes": [
                "mcp:read",
                "content:write",
                "media:write",
                "publishing:write",
                "content:delete"
            ]
        }
    },
    "tools": [
        {
            "name": "get_overview",
            "title": "Overview",
            "description": "What needs attention across the properties you can see: drafts and outlines waiting for review, failed sends, live posts with unpublished changes, upcoming scheduled posts, and AI drafts left this month.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "property": {
                        "type": "string",
                        "description": "Optional: one property, by its id (prp_…) or part of its name or web address. Leave out for all."
                    }
                }
            },
            "requiredScope": "mcp:read"
        },
        {
            "name": "list_review_queue",
            "title": "Review queue",
            "description": "Drafts and outlines waiting for review, oldest first, with each one's Article Health problems. Use for \"what posts are pending my review\" or \"what outlines are waiting\".",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "property": {
                        "type": "string",
                        "description": "Optional: one property, by its id (prp_…) or part of its name or web address. Leave out for all."
                    },
                    "kind": {
                        "type": "string",
                        "enum": [
                            "all",
                            "drafts",
                            "outlines"
                        ],
                        "description": "Drafts, outlines, or both (default)."
                    }
                }
            },
            "requiredScope": "mcp:read"
        },
        {
            "name": "list_articles",
            "title": "Articles",
            "description": "Articles by status. Status: all, outline, writing, draft, review, approved, scheduled (not yet sent), live, unpublished_changes, failed. Sorted newest first by publish date for live articles, and by last update otherwise; set sort to choose. Failed articles include the last send's error.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "property": {
                        "type": "string",
                        "description": "Optional: one property, by its id (prp_…) or part of its name or web address. Leave out for all."
                    },
                    "status": {
                        "type": "string"
                    },
                    "search": {
                        "type": "string",
                        "description": "Words in the title."
                    },
                    "sort": {
                        "type": "string",
                        "enum": [
                            "published",
                            "updated"
                        ],
                        "description": "published: newest publish date first (articles not yet published last). updated: most recently changed first. Default: published for live, updated otherwise."
                    },
                    "limit": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 100
                    }
                }
            },
            "requiredScope": "mcp:read"
        },
        {
            "name": "get_article",
            "title": "Article",
            "description": "One article: status, brief, outline, Article Health, approval, scheduled date and links. Set include_body for its text.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "The article id (cnt_…)."
                    },
                    "include_body": {
                        "type": "boolean"
                    }
                },
                "required": [
                    "id"
                ]
            },
            "requiredScope": "mcp:read"
        },
        {
            "name": "get_recent_performance",
            "title": "Recent performance",
            "description": "The most recently published live posts and how they're doing: visitors, views, reading time and key events (Google Analytics), and search clicks, impressions and position (Search Console), for the last 30 days.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "property": {
                        "type": "string",
                        "description": "Optional: one property, by its id (prp_…) or part of its name or web address. Leave out for all."
                    },
                    "limit": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 50
                    }
                }
            },
            "requiredScope": "mcp:read"
        },
        {
            "name": "get_article_performance",
            "title": "Article performance",
            "description": "One live article's figures for 7, 30 and 90 days, where its visitors came from, its top searches, and whether Google has indexed it.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "The article id (cnt_…)."
                    }
                },
                "required": [
                    "id"
                ]
            },
            "requiredScope": "mcp:read"
        },
        {
            "name": "list_content_queue",
            "title": "Content Queue",
            "description": "Topics in the queue, in order: each one's status (such as \"Outline ready: needs review\"), its article id and title once one exists, its planned date when a schedule is on, and what happens at that time (unapproved articles are held, never published).",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "property": {
                        "type": "string",
                        "description": "Optional: one property, by its id (prp_…) or part of its name or web address. Leave out for all."
                    },
                    "limit": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 100
                    }
                }
            },
            "requiredScope": "mcp:read"
        },
        {
            "name": "get_insights",
            "title": "Insights",
            "description": "The top recommendations for getting more from Google Search, such as titles that could earn more clicks, with the estimated extra clicks each.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "property": {
                        "type": "string",
                        "description": "Optional: one property, by its id (prp_…) or part of its name or web address. Leave out for all."
                    },
                    "limit": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 30
                    }
                }
            },
            "requiredScope": "mcp:read"
        },
        {
            "name": "list_properties",
            "title": "Properties",
            "description": "The properties (websites) you can see, with their account, platform, connection status and your role.",
            "inputSchema": {
                "type": "object",
                "properties": {}
            },
            "requiredScope": "mcp:read"
        },
        {
            "name": "get_writing_guide",
            "title": "Writing guide",
            "description": "A property's writing settings: audience, voice, length, default categories and tags, call to action, words to avoid, approval rules, and AI drafts left this month. Read this before create_draft, so you only ask the person about what the property doesn't already settle.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "property": {
                        "type": "string",
                        "description": "The property (website): its id (prp_...) or part of its name or web address."
                    }
                },
                "required": [
                    "property"
                ]
            },
            "requiredScope": "mcp:read"
        },
        {
            "name": "list_categories",
            "title": "Categories and tags",
            "description": "The categories and tags a property's site has, and the property's defaults. Use the site's own names exactly.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "property": {
                        "type": "string",
                        "description": "The property (website): its id (prp_...) or part of its name or web address."
                    }
                },
                "required": [
                    "property"
                ]
            },
            "requiredScope": "mcp:read"
        },
        {
            "name": "list_images",
            "title": "Image library",
            "description": "Images in a property's library, newest first: id (ast_...), title, alt text, tags, size and address. Search matches title, alt text and tags.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "property": {
                        "type": "string",
                        "description": "The property (website): its id (prp_...) or part of its name or web address."
                    },
                    "search": {
                        "type": "string"
                    },
                    "limit": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 100
                    }
                },
                "required": [
                    "property"
                ]
            },
            "requiredScope": "mcp:read"
        },
        {
            "name": "create_draft",
            "title": "Create a draft",
            "description": "Start a new article. Two ways: writer \"ai\" has ABCDEF Studio write it in the property's voice (outline first, if the property reviews outlines), which uses one AI draft from the monthly allowance; writer \"provided\" saves a title and body you supply. Before calling, read get_writing_guide, then ask the person, in ONE message, for everything below that they haven't told you: the topic, the key points or facts to cover, the audience, what readers should do afterward, the main search keyword, the length, pages it must link to, other pages it may link to, categories, tags, and who writes it. Offer the property's defaults where it has them. Use \"default\" to take a property default and \"none\" where the answer is none. Never invent facts. If anything is missing, this returns needs_input with the questions to ask instead of creating anything. Nothing reaches the site.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "property": {
                        "type": "string",
                        "description": "The property (website): its id (prp_...) or part of its name or web address."
                    },
                    "topic": {
                        "type": "string",
                        "description": "What the article is about, or its working title."
                    },
                    "key_points": {
                        "type": "string",
                        "description": "The points, facts, figures or angle it must cover, from the person. \"none\" lets the writer decide."
                    },
                    "audience": {
                        "type": "string",
                        "description": "Who it's for, or \"default\"."
                    },
                    "purpose": {
                        "type": "string",
                        "description": "What readers should do or understand afterward (the call to action), or \"default\"."
                    },
                    "primary_keyword": {
                        "type": "string",
                        "description": "The main search phrase, or \"none\"."
                    },
                    "target_words": {
                        "description": "Length in words, or \"default\".",
                        "oneOf": [
                            {
                                "type": "integer"
                            },
                            {
                                "type": "string"
                            }
                        ]
                    },
                    "required_links": {
                        "type": "string",
                        "description": "Pages the article must link to (addresses, one per line), or \"none\". Each one goes in the article; Article Health flags any that are missing."
                    },
                    "links": {
                        "type": "string",
                        "description": "Other pages it may link to where they fit (addresses, one per line), or \"none\"."
                    },
                    "categories": {
                        "description": "Category names from list_categories, or \"default\".",
                        "oneOf": [
                            {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                }
                            },
                            {
                                "type": "string"
                            }
                        ]
                    },
                    "tags": {
                        "description": "Tags, or \"default\", or \"none\".",
                        "oneOf": [
                            {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                }
                            },
                            {
                                "type": "string"
                            }
                        ]
                    },
                    "writer": {
                        "type": "string",
                        "enum": [
                            "ai",
                            "provided"
                        ],
                        "description": "\"ai\": ABCDEF Studio writes it. \"provided\": you supply title and body_html."
                    },
                    "title": {
                        "type": "string",
                        "description": "For writer \"provided\": the title."
                    },
                    "body_html": {
                        "type": "string",
                        "description": "For writer \"provided\": the article body as HTML (h2, h3, p, ul, ol, li, a, strong, em, blockquote). No h1: the title is separate. No images: an article has one featured image (set_featured_image)."
                    },
                    "excerpt": {
                        "type": "string",
                        "description": "Optional for \"provided\": a one- or two-sentence summary."
                    },
                    "meta_description": {
                        "type": "string",
                        "description": "Optional for \"provided\": about 150 characters for search results."
                    },
                    "notes": {
                        "type": "string",
                        "description": "Optional: anything else the writer should know."
                    }
                },
                "required": [
                    "property"
                ]
            },
            "requiredScope": "content:write"
        },
        {
            "name": "update_draft",
            "title": "Edit an article",
            "description": "Change an article in ABCDEF Studio: title, body, excerpt, meta description, SEO title, slug, categories or tags. Fields you leave out stay as they are. Or set ai_instructions to have ABCDEF Studio rewrite it (uses one AI draft). Each edit is a new revision; an approved article needs approving again. Edits to a live article stay in ABCDEF Studio until someone publishes them (publish_post); changing a live article's slug changes its address, so it needs confirm_slug_change.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "The article id (cnt_...)."
                    },
                    "title": {
                        "type": "string"
                    },
                    "body_html": {
                        "type": "string",
                        "description": "The article body as HTML. No images in the text: use set_featured_image."
                    },
                    "excerpt": {
                        "type": "string"
                    },
                    "meta_description": {
                        "type": "string"
                    },
                    "seo_title": {
                        "type": "string"
                    },
                    "slug": {
                        "type": "string"
                    },
                    "categories": {
                        "description": "Category names.",
                        "oneOf": [
                            {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                }
                            },
                            {
                                "type": "string"
                            }
                        ]
                    },
                    "tags": {
                        "description": "Tags.",
                        "oneOf": [
                            {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                }
                            },
                            {
                                "type": "string"
                            }
                        ]
                    },
                    "note": {
                        "type": "string",
                        "description": "What changed, for the revision history."
                    },
                    "ai_instructions": {
                        "type": "string",
                        "description": "Have ABCDEF Studio rewrite the draft following these instructions, instead of applying field edits."
                    },
                    "confirm_slug_change": {
                        "type": "boolean"
                    }
                },
                "required": [
                    "id"
                ]
            },
            "requiredScope": "content:write"
        },
        {
            "name": "write_draft_from_outline",
            "title": "Write from the outline",
            "description": "For an article whose outline is waiting for review: approve the outline and have ABCDEF Studio write the draft from it (uses one AI draft). Optional instructions guide the writing.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "The article id (cnt_...)."
                    },
                    "instructions": {
                        "type": "string"
                    }
                },
                "required": [
                    "id"
                ]
            },
            "requiredScope": "content:write"
        },
        {
            "name": "add_topic",
            "title": "Add a topic",
            "description": "Add a topic to a property's Content Queue. If the property has a schedule, ABCDEF Studio writes it when its turn comes. Ask the person for a summary of what it should cover if they haven't given one.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "property": {
                        "type": "string",
                        "description": "The property (website): its id (prp_...) or part of its name or web address."
                    },
                    "title": {
                        "type": "string"
                    },
                    "summary": {
                        "type": "string",
                        "description": "What it should cover."
                    },
                    "required_links": {
                        "type": "string",
                        "description": "Pages its article must link to (addresses, one per line). They carry into the brief when it's written, and Article Health flags any that are missing."
                    },
                    "links": {
                        "type": "string",
                        "description": "Other pages its article may link to where they fit (addresses, one per line)."
                    },
                    "where": {
                        "type": "string",
                        "enum": [
                            "bottom",
                            "top"
                        ],
                        "description": "End of the queue (default) or next up."
                    }
                },
                "required": [
                    "property",
                    "title"
                ]
            },
            "requiredScope": "content:write"
        },
        {
            "name": "update_topic",
            "title": "Edit a topic",
            "description": "Change a Content Queue topic: its title, summary, or the pages its article must or may link to. Fields you leave out stay as they are; \"none\" clears a list. The topic id (tpc_...) comes from list_content_queue, which also shows what is saved. If its article has already been started, the links are added to that article's brief too.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "The topic id (tpc_...)."
                    },
                    "title": {
                        "type": "string"
                    },
                    "summary": {
                        "type": "string"
                    },
                    "required_links": {
                        "type": "string",
                        "description": "Pages its article must link to (addresses, one per line), or \"none\"."
                    },
                    "links": {
                        "type": "string",
                        "description": "Other pages its article may link to (addresses, one per line), or \"none\"."
                    }
                },
                "required": [
                    "id"
                ]
            },
            "requiredScope": "content:write"
        },
        {
            "name": "set_categories",
            "title": "Set categories and tags",
            "description": "Replace an article's categories and/or tags (a new revision). Use names from list_categories.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "The article id (cnt_...)."
                    },
                    "categories": {
                        "description": "Category names. Leave out to keep them.",
                        "oneOf": [
                            {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                }
                            },
                            {
                                "type": "string"
                            }
                        ]
                    },
                    "tags": {
                        "description": "Tags. Leave out to keep them.",
                        "oneOf": [
                            {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                }
                            },
                            {
                                "type": "string"
                            }
                        ]
                    }
                },
                "required": [
                    "id"
                ]
            },
            "requiredScope": "content:write"
        },
        {
            "name": "upload_image",
            "title": "Add an image",
            "description": "Add an image (JPEG, PNG, WebP or GIF, up to 20 MB) to a property's image library, for use as a featured image (an article has one; images aren't placed in article text), from a public https address or as base64. By default it's cropped to the 1200x630 featured image size: look at the image and set focus to keep its subject in the frame (top for a portrait photo of a face, say). Use shape \"fit\" to keep the whole image on white bars, or \"keep\" for its own proportions. Optionally make it an article's featured image. Ask the person for alt text describing the image if you can't see it.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "property": {
                        "type": "string",
                        "description": "The property (website): its id (prp_...) or part of its name or web address."
                    },
                    "image_url": {
                        "type": "string"
                    },
                    "image_base64": {
                        "type": "string"
                    },
                    "filename": {
                        "type": "string"
                    },
                    "title": {
                        "type": "string"
                    },
                    "alt": {
                        "type": "string",
                        "description": "Describes the image for people who can't see it."
                    },
                    "tags": {
                        "description": "Subject words, so ABCDEF Studio can match it to articles.",
                        "oneOf": [
                            {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                }
                            },
                            {
                                "type": "string"
                            }
                        ]
                    },
                    "featured_for": {
                        "type": "string",
                        "description": "An article id (cnt_...) to make this its featured image."
                    },
                    "shape": {
                        "type": "string",
                        "enum": [
                            "crop",
                            "fit",
                            "keep"
                        ],
                        "description": "\"crop\" (default): crop to 1200x630. \"fit\": the whole image inside 1200x630 on white bars. \"keep\": its own proportions (long edge up to 2400px)."
                    },
                    "focus": {
                        "type": "string",
                        "enum": [
                            "center",
                            "top",
                            "bottom",
                            "left",
                            "right"
                        ],
                        "description": "For \"crop\": which part to keep. Top or bottom for a tall image, left or right for a wide one. Default center."
                    }
                },
                "required": [
                    "property"
                ]
            },
            "requiredScope": "media:write"
        },
        {
            "name": "set_featured_image",
            "title": "Set the featured image",
            "description": "Make an image from the property's library (list_images) an article's featured image, or clear it with image \"none\". A new revision.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "The article id (cnt_...)."
                    },
                    "image": {
                        "type": "string",
                        "description": "The image id (ast_...), or \"none\"."
                    },
                    "alt": {
                        "type": "string"
                    }
                },
                "required": [
                    "id",
                    "image"
                ]
            },
            "requiredScope": "media:write"
        },
        {
            "name": "publish_post",
            "title": "Publish",
            "description": "Publish an article live on its site now (or, with as \"site_draft\", send it to the site as an unpublished draft). Also publishes pending edits to a live article. First call returns a preview: what will be sent where, Article Health, and whether it needs approving (if so, confirming approves the current revision as the person). Show the preview and ask; call again with confirm true only after they agree.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "The article id (cnt_...)."
                    },
                    "as": {
                        "type": "string",
                        "enum": [
                            "live",
                            "site_draft"
                        ]
                    },
                    "confirm": {
                        "type": "boolean",
                        "description": "Leave out (or false) to get a preview of exactly what will happen. Show it to the person; call again with true only after they agree."
                    }
                },
                "required": [
                    "id"
                ]
            },
            "requiredScope": "publishing:write"
        },
        {
            "name": "schedule_post",
            "title": "Schedule",
            "description": "Publish an article live at a set time, or cancel its scheduled time (cancel true). Times are in the property's time zone unless you include an offset, such as \"2026-10-13 09:00\" or \"2026-10-13T09:00:00-07:00\". Ask the person for the date and time if they haven't given both, and confirm the time zone. First call returns a preview (including approval, which confirming grants as the person); call again with confirm true after they agree. At that time it is checked again, and held, not published, if something changed.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "The article id (cnt_...)."
                    },
                    "at": {
                        "type": "string"
                    },
                    "cancel": {
                        "type": "boolean"
                    },
                    "confirm": {
                        "type": "boolean",
                        "description": "Leave out (or false) to get a preview of exactly what will happen. Show it to the person; call again with true only after they agree."
                    }
                },
                "required": [
                    "id"
                ]
            },
            "requiredScope": "publishing:write"
        },
        {
            "name": "unpublish_post",
            "title": "Unpublish",
            "description": "Take a live article off its site, keeping it there as a draft (and in ABCDEF Studio). Not every site connection can; the preview says. First call previews; confirm true acts.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "The article id (cnt_...)."
                    },
                    "confirm": {
                        "type": "boolean",
                        "description": "Leave out (or false) to get a preview of exactly what will happen. Show it to the person; call again with true only after they agree."
                    }
                },
                "required": [
                    "id"
                ]
            },
            "requiredScope": "publishing:write"
        },
        {
            "name": "delete_article",
            "title": "Delete an article",
            "description": "Remove an article from ABCDEF Studio. A live article stays on its site unless remove_from_site is true (which also needs the Publish permission; on Shopify that deletes it for good). First call previews; confirm true acts.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "The article id (cnt_...)."
                    },
                    "remove_from_site": {
                        "type": "boolean"
                    },
                    "confirm": {
                        "type": "boolean",
                        "description": "Leave out (or false) to get a preview of exactly what will happen. Show it to the person; call again with true only after they agree."
                    }
                },
                "required": [
                    "id"
                ]
            },
            "requiredScope": "content:delete"
        },
        {
            "name": "delete_image",
            "title": "Delete an image",
            "description": "Remove an image from a property's library. An image an article has used is archived rather than deleted, so its history stays. First call previews; confirm true acts.",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "property": {
                        "type": "string",
                        "description": "The property (website): its id (prp_...) or part of its name or web address."
                    },
                    "image": {
                        "type": "string",
                        "description": "The image id (ast_...)."
                    },
                    "confirm": {
                        "type": "boolean",
                        "description": "Leave out (or false) to get a preview of exactly what will happen. Show it to the person; call again with true only after they agree."
                    }
                },
                "required": [
                    "property",
                    "image"
                ]
            },
            "requiredScope": "content:delete"
        }
    ]
}