Koskamo

SubmitMap MCP

An MCP server providing access to a directory of 848 product launch platforms. It allows agents to search platforms, qualify projects for eligibility, retrieve submission instructions, and with a user token, store product details, plan submissions, generate filled form briefs, and record submission results to the user's dashboard.

Endpoint and access

No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.

SubmitMap MCP logo, by SubmitMapVendor evidenceStreamable HTTPNo sign-inEndpoint checked 2026-11-06Operated by SubmitMap
You need:token from https://submitmap.com/app/tokens/
MCP endpoint (Streamable HTTP)
https://submitmap.com/api/mcp
Vendor documentation

What its tools do

11 tools, grouped by the hints the server advertises. Hints are self-declared, not verified behavior.

11 of 11 have a description · 0 of 11 declare behavior hints · 3 of 9 with parameters document them all

Checks and signals

Each line is separate evidence, not a trust score.

  • Passed Endpoint answered the live check ·
  • Passed Official MCP Registry namespace is tied to submitmap.com ·
  • Passed Endpoint is on the registry namespace domain ·
  • Availability 1 of 1 checks passed in 30 days

What you can do with it

  • Search for product launch platforms
  • Qualify a project for platform eligibility
  • Generate filled submission form briefs
  • Record submission results to dashboard

Source and maintenance

Vendor evidenceRegistry 1.0.0
Maintenance checked

Protocol and capabilities

Server
submitmap 2.0.0
MCP protocol negotiated in check
2025-06-18
Tools
11 advertised
Resources
Not advertised
Resource templates
Not advertised
Prompts
Not advertised
Server-advertised capabilities
{
  "tools": {}
}

Server-provided instructions

Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.

SubmitMap is a directory of startup launch platforms. Use qualify_project to find where a product can be submitted and what is missing, then get_platform for the submission steps. Filling the forms is the account half: the per-form brief, the playbooks and submission tracking all need a token from https://submitmap.com/docs/setup/. When something here goes wrong, do not leave the maker with the error: the reference is at https://submitmap.com/docs/, and anything the docs do not cover, a playbook that no longer matches the real form, a limit that counted wrongly, a tool that keeps failing, goes to https://submitmap.com/support/?topic=agent, which takes a message with or without an account.

Tools

11 advertised

search_platforms
Advertised by the server during the last successful anonymous check.

Search the SubmitMap directory of startup launch platforms and directories. Filter by free-text query, category, pricing, link type, backlink requirement, approval speed or domain rating, and with a token leave out the ones this project has already been sent to. This is the tool behind "find me a small directory I can submit to right now": it is the whole directory, so answer from it rather than from what you remember of the web. Returns summaries; call get_platform for the full record including the submission steps.

search_platforms input schema
{
  "type": "object",
  "properties": {
    "sort": {
      "enum": [
        "approvalDays",
        "domainRating"
      ],
      "type": "string",
      "description": "Fastest approval first (the default), or strongest domain first. Either way, a platform that publishes no approval time comes last rather than reading as instant."
    },
    "limit": {
      "type": "number",
      "description": "Return at most this many. The count is unaffected."
    },
    "query": {
      "type": "string",
      "description": "Free text matched against name, tagline, domain, slug and categories. A bare domain works: \"startupfa.me\" finds Startup Fame."
    },
    "pricing": {
      "enum": [
        "free",
        "freemium",
        "paid"
      ],
      "type": "string"
    },
    "category": {
      "type": "string",
      "description": "e.g. \"AI tools\", \"Dev tools\", \"Web apps\"."
    },
    "dofollow": {
      "type": "boolean",
      "description": "Only platforms giving a dofollow backlink."
    },
    "projectId": {
      "type": "string",
      "description": "Omit it when the account has one project, which is the free tier."
    },
    "excludeTracked": {
      "type": "boolean",
      "description": "Leave out every platform this project has already been submitted to or planned for. Needs a token, and is what makes \"one I have not done yet\" answerable."
    },
    "maxApprovalDays": {
      "type": "number",
      "description": "Only platforms approving within this many days. Leaves out every platform that publishes no approval time, the same way a rating bound leaves out the unrated."
    },
    "maxDomainRating": {
      "type": "number",
      "description": "Only platforms up to this domain rating. The small, quick, unglamorous end of the directory, which is where a first submission usually belongs."
    },
    "minDomainRating": {
      "type": "number",
      "description": "Only platforms with at least this domain rating."
    },
    "requiresBacklink": {
      "type": "boolean",
      "description": "Filter on whether they demand a badge/link back."
    }
  }
}
get_platform
Advertised by the server during the last successful anonymous check.

Full record for one platform: eligibility, disqualifiers, step-by-step submission instructions, requirements, gotchas and expected outcome. All of that is free, and it is what decides whether this product should be submitted here at all. Pass brief: true to also get agentPrompt, the brief for that platform's form, and agentGotchas, the traps that only bite something filling it (a placeholder that contradicts its label, a second email input belonging to a newsletter, a honeypot). The brief spends one of the account's tracked platforms, so ask for it once the maker has decided to fill this form, not while you are still choosing between platforms. For a stored project, submission_playbook returns the same brief with the maker's values already in it.

get_platform input schema
{
  "type": "object",
  "required": [
    "slug"
  ],
  "properties": {
    "slug": {
      "type": "string",
      "description": "Platform slug, e.g. \"product-hunt\"."
    },
    "brief": {
      "type": "boolean",
      "description": "Include agentPrompt and agentGotchas. This is the paid half and it spends a tracked platform, once, for good. Leave it out while you are deciding where to submit: eligibility, disqualifiers, gotchas, approval time and link type all come back either way. A platform this account has already spent a slot on is free to brief again, so re-reading one costs nothing."
    },
    "projectId": {
      "type": "string",
      "description": "Which project the brief is for, when brief is true. Omit it when the account has one project. A brief is one form being worked on for one product, and the project it belongs to cannot be issued another until this one has an outcome recorded."
    }
  }
}
qualify_project
Advertised by the server during the last successful anonymous check.

Given a product, work out which platforms it qualifies for right now, which it could qualify for after supplying something (with the exact list of what is missing), and which are structurally out of reach. It also hands back `recommended`: the run in the order it should be worked, so the plan comes out of the directory rather than out of what you remember of the web. That order is decided by `goal` and `budget`, and when they have not been answered the response says what it assumed and carries the questions to put to the maker: ask them, call again with the answers, and store them with update_project so nobody asks twice. Needs no account: describe the product inline. Every field is optional and an unanswered field becomes a gap to fill rather than a rejection.

qualify_project input schema
{
  "type": "object",
  "properties": {
    "url": {
      "type": "string"
    },
    "docs": {
      "type": "boolean"
    },
    "goal": {
      "enum": [
        "already-live",
        "launching-later"
      ],
      "type": "string",
      "description": "What this run is for, which decides the order of it. `already-live` means the product is out and wants listings, links and traffic now, so the run leads on fast approvals, dofollow links and strong domains. `launching-later` means there is a launch date ahead, so it leads on the platforms with months of queue. Ask the maker rather than guessing: absent, it is inferred from `stage` and the response says so."
    },
    "name": {
      "type": "string"
    },
    "stage": {
      "enum": [
        "idea",
        "prelaunch",
        "live",
        "revenue"
      ],
      "type": "string"
    },
    "budget": {
      "enum": [
        "free-only",
        "will-pay"
      ],
      "type": "string",
      "description": "Whether platforms that charge to list may appear in the run. Absent means `free-only`, which is the assumption that cannot cost anybody anything. `freemium` platforms are in the run either way: their submission is free, and paying to skip their queue is a separate question at the form."
    },
    "launchAt": {
      "type": "string",
      "description": "ISO 8601 date the maker is launching on, when they have named one."
    },
    "demoVideo": {
      "type": "boolean"
    },
    "categories": {
      "type": "array",
      "items": {
        "enum": [
          "agent-skill",
          "ai",
          "b2b",
          "christian",
          "community",
          "content",
          "design",
          "dev",
          "directory",
          "extension",
          "free",
          "hardware",
          "indie",
          "local",
          "marketing",
          "marketplace",
          "mcp",
          "mobile",
          "no-code",
          "open-source",
          "prelaunch",
          "productivity",
          "saas",
          "turkish-founders",
          "web"
        ],
        "type": "string"
      },
      "description": "Every audience and form factor this product belongs to, not just the main one. A product is often several at once, and one missing tag is one directory it never hears about: a repository holding a browser extension and a web app is `extension` and `web`, and an open-source AI dev tool is `open-source`, `ai` and `dev`. Look at what the product actually ships before you answer: a manifest.json means `extension`, an ios or android target means `mobile`, a public repository means `open-source`, an mcp server is `mcp`, a workspace with several of those means all of them. Then answer what it is commercially, which is a separate question and the one most runs lose platforms on: most launch boards say what they take by who ships it, not by what it runs on, so a subscription mobile app is `mobile` and `saas`, anything a solo maker or small team shipped is `indie`, and something sold to companies is `b2b`. Leaving those off does not make the run safer, it makes it shorter. Tags: agent-skill, ai, b2b, christian, community, content, design, dev, directory, extension, free, hardware, indie, local, marketing, marketplace, mcp, mobile, no-code, open-source, prelaunch, productivity, saas, turkish-founders, web."
    },
    "coverImage": {
      "type": "boolean"
    },
    "logoSquare": {
      "type": "boolean"
    },
    "openSignup": {
      "type": "boolean"
    },
    "publicRepo": {
      "type": "boolean"
    },
    "pricingPage": {
      "type": "boolean"
    },
    "domainRating": {
      "type": "number"
    },
    "pricingModel": {
      "enum": [
        "free",
        "freemium",
        "paid",
        "lifetime"
      ],
      "type": "string"
    },
    "livePublicUrl": {
      "type": "boolean"
    },
    "privacyPolicy": {
      "type": "boolean"
    },
    "monthlyTraffic": {
      "type": "number"
    },
    "acceptsBacklink": {
      "type": "boolean"
    },
    "payingCustomers": {
      "type": "boolean"
    }
  }
}
whoami
Advertised by the server during the last successful anonymous check.

Which SubmitMap account this token belongs to, named by its email address, which plan it is on, and what it already holds. Call it first if you are unsure whether the maker is connected, and tell them the address it reports: it is the only way either of you can tell one of their accounts from another. It does not hand you the plan's limits and you do not need them to work: plan the whole run, and a limit says so at the write it stops.

whoami input schema
{
  "type": "object",
  "properties": {}
}
list_projects
Advertised by the server during the last successful anonymous check.

The projects on this account, each with its facts (eligibility answers) and its pack (what a submission form asks for), plus what is still missing from the pack. Call it whenever the maker says "my product" or "my project" without naming one: it is how you find out which project they mean, and every other account tool takes the id it returns.

list_projects input schema
{
  "type": "object",
  "properties": {}
}
create_project
Advertised by the server during the last successful anonymous check.

Store a product on the account. Before asking the maker anything, use what you can already see: a README, package metadata, the site's own copy and title, an assets or public folder. A maker working in their product's repository should be able to say 'add my product to SubmitMap' and get a filled-in project back, with questions only about what is genuinely not there. Fill in as much as you can, leave the rest, and come back with update_project. Facts drive what it qualifies for; the pack is what you will paste into forms later. Ask about images early: most platforms want a square logo and many want a cover, and every asset in the pack is two fields, the public address (logoUrl) and the file on the maker's machine (logoFile), because a form uploads the file and only the address can be shown back to them.

create_project input schema
{
  "type": "object",
  "required": [
    "name"
  ],
  "properties": {
    "url": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "pack": {
      "type": "object",
      "properties": {
        "url": {
          "type": "string",
          "description": "The page a visitor lands on, not a waitlist."
        },
        "name": {
          "type": "string",
          "description": "Exactly as it should appear in a listing. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "logoUrl": {
          "type": "string",
          "description": "Square, transparent, 512px or better. The public address of the image: forms with a logo URL field take this one, and it is what shows on your dashboard."
        },
        "pricing": {
          "type": "string",
          "description": "What it costs, in a sentence a form can take. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "repoUrl": {
          "type": "string",
          "description": "The public repository, for open-source directories. Leave it empty for a closed-source product rather than pointing at something else."
        },
        "tagline": {
          "type": "string",
          "description": "One line, no product name, no full stop. Max 60 characters. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "xHandle": {
          "type": "string",
          "description": "Without the @. Launch boards ask for it and some make it a required field, so a pack without one stalls at the last step of a form that is otherwise finished. Fill it only with an account you hold: it is printed on the listing as the maker, and a handle typed to satisfy a validator credits whoever really owns it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "audience": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "The people it is built for, like Freelancers or Small businesses, for the forms with a target-audience picker. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "coverUrl": {
          "type": "string",
          "description": "1270x760, the size every gallery accepts. The public address of it."
        },
        "location": {
          "type": "string",
          "description": "City and country, or country alone. Asked for as headquarters, and it decides the regional directories. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "logoFile": {
          "type": "string",
          "description": "The same logo as a file on the machine your agent runs on, like /Users/you/product/logo-512.png. This is the one a submission form uploads, because a file picker cannot be handed a link. It stays a path: nothing can display it, here or anywhere else."
        },
        "makerBio": {
          "type": "string",
          "description": "Asked for by launch platforms, not by plain directories. Max 160 characters. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "oneLiner": {
          "type": "string",
          "description": "A sentence that works as a meta description. Max 140 characters. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "teamSize": {
          "type": "string",
          "description": "How many people work on it, as a number or a range like 2-10. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "coverFile": {
          "type": "string",
          "description": "The same cover as a file on the machine your agent runs on, for the forms that upload it."
        },
        "makerName": {
          "type": "string",
          "description": "The person shown as the maker, first and last name, exactly as it should be printed on a listing. Launch boards and founder profiles require it. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "makerRole": {
          "type": "string",
          "description": "Their title, like Founder or Solo developer. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "techStack": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "What it is built with, five at most, like Next.js or Postgres. Launch boards for builders make it a required step. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "buildStory": {
          "type": "string",
          "description": "Why and how it was built, with the real numbers, for community posts and maker showcases that want a story rather than a description. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "categories": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Three at most, in the platform's own vocabulary where it has one. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "howItWorks": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Three short steps, one per line, under 200 characters each. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "companyName": {
          "type": "string",
          "description": "The business behind the product, when it is not the product name. Review sites and B2B directories list the company. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "foundedYear": {
          "type": "string",
          "description": "Four digits. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "galleryUrls": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Three to five, first one a poster slide. Public addresses, one per line. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "contactEmail": {
          "type": "string",
          "description": "The address you submit under. Most forms ask for one, and it is where the confirmation link lands, so a mailbox nobody opens stalls the submission. Per product, not per person: a client launch and a side project rarely want the same address, and the one you use here is the one the listing belongs to. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "demoVideoUrl": {
          "type": "string",
          "description": "Optional almost everywhere, decisive on a few. A watch page or a hosted file."
        },
        "firstComment": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Product Hunt and Show HN only, and it matters there. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "fundingStage": {
          "type": "string",
          "description": "Bootstrapped, or what was raised and at which stage. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "galleryFiles": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "The same frames as files on the machine your agent runs on, in the same order, one per line. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "alternativeTo": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Well-known products this replaces, by name. Alternative directories will not take a listing without one. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "demoVideoFile": {
          "type": "string",
          "description": "The video as a file on the machine your agent runs on, for the few platforms that upload rather than embed."
        },
        "monthlyRevenue": {
          "type": "string",
          "description": "MRR or a bracket like $0-1k, and only if the maker is happy for it to be public: forms that ask for it usually display it. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "longDescription": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Two or three paragraphs, for the forms with room. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "monthlyVisitors": {
          "type": "string",
          "description": "A real figure from their analytics. Some directories make it a required number and print it. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "shortDescription": {
          "type": "string",
          "description": "The paragraph most directory forms ask for. Max 260 characters. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "extendedDescription": {
          "type": "string",
          "description": "A full write-up of 400 words or more (about 2,500 characters) for the forms that set a minimum length. The long description above is shorter on purpose. Drafting it from the product is fine, but show the maker before it is used, because it goes out under their name. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        }
      }
    },
    "facts": {
      "type": "object",
      "properties": {
        "stage": {
          "enum": [
            "idea",
            "prelaunch",
            "live",
            "revenue"
          ],
          "type": "string"
        },
        "categories": {
          "type": "array",
          "items": {
            "enum": [
              "agent-skill",
              "ai",
              "b2b",
              "christian",
              "community",
              "content",
              "design",
              "dev",
              "directory",
              "extension",
              "free",
              "hardware",
              "indie",
              "local",
              "marketing",
              "marketplace",
              "mcp",
              "mobile",
              "no-code",
              "open-source",
              "prelaunch",
              "productivity",
              "saas",
              "turkish-founders",
              "web"
            ],
            "type": "string"
          },
          "description": "Every audience and form factor this product belongs to, not just the main one. A product is often several at once, and one missing tag is one directory it never hears about: a repository holding a browser extension and a web app is `extension` and `web`, and an open-source AI dev tool is `open-source`, `ai` and `dev`. Look at what the product actually ships before you answer: a manifest.json means `extension`, an ios or android target means `mobile`, a public repository means `open-source`, an mcp server is `mcp`, a workspace with several of those means all of them. Then answer what it is commercially, which is a separate question and the one most runs lose platforms on: most launch boards say what they take by who ships it, not by what it runs on, so a subscription mobile app is `mobile` and `saas`, anything a solo maker or small team shipped is `indie`, and something sold to companies is `b2b`. Leaving those off does not make the run safer, it makes it shorter. Tags: agent-skill, ai, b2b, christian, community, content, design, dev, directory, extension, free, hardware, indie, local, marketing, marketplace, mcp, mobile, no-code, open-source, prelaunch, productivity, saas, turkish-founders, web."
        },
        "pricingModel": {
          "enum": [
            "free",
            "freemium",
            "paid",
            "lifetime"
          ],
          "type": "string"
        }
      },
      "description": "Same shape qualify_project takes: stage, pricingModel, categories, and the booleans. It is also where the two answers that decide a run order live once the maker has given them (`goal`, `budget`, and `launchAt` if they named a date), which is what stops the next session asking again. Leave domainRating out: the url is measured against Ahrefs on the way in and the measured number is what gets stored, so a guess here is overwritten anyway."
    },
    "pitch": {
      "type": "string",
      "description": "One paragraph in the maker's own words."
    }
  }
}
update_project
Advertised by the server during the last successful anonymous check.

Fill in or correct a stored project. Facts and pack are merged into what is there, so you can add one field at a time as the maker answers.

update_project input schema
{
  "type": "object",
  "properties": {
    "url": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "pack": {
      "type": "object",
      "properties": {
        "url": {
          "type": "string",
          "description": "The page a visitor lands on, not a waitlist."
        },
        "name": {
          "type": "string",
          "description": "Exactly as it should appear in a listing. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "logoUrl": {
          "type": "string",
          "description": "Square, transparent, 512px or better. The public address of the image: forms with a logo URL field take this one, and it is what shows on your dashboard."
        },
        "pricing": {
          "type": "string",
          "description": "What it costs, in a sentence a form can take. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "repoUrl": {
          "type": "string",
          "description": "The public repository, for open-source directories. Leave it empty for a closed-source product rather than pointing at something else."
        },
        "tagline": {
          "type": "string",
          "description": "One line, no product name, no full stop. Max 60 characters. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "xHandle": {
          "type": "string",
          "description": "Without the @. Launch boards ask for it and some make it a required field, so a pack without one stalls at the last step of a form that is otherwise finished. Fill it only with an account you hold: it is printed on the listing as the maker, and a handle typed to satisfy a validator credits whoever really owns it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "audience": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "The people it is built for, like Freelancers or Small businesses, for the forms with a target-audience picker. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "coverUrl": {
          "type": "string",
          "description": "1270x760, the size every gallery accepts. The public address of it."
        },
        "location": {
          "type": "string",
          "description": "City and country, or country alone. Asked for as headquarters, and it decides the regional directories. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "logoFile": {
          "type": "string",
          "description": "The same logo as a file on the machine your agent runs on, like /Users/you/product/logo-512.png. This is the one a submission form uploads, because a file picker cannot be handed a link. It stays a path: nothing can display it, here or anywhere else."
        },
        "makerBio": {
          "type": "string",
          "description": "Asked for by launch platforms, not by plain directories. Max 160 characters. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "oneLiner": {
          "type": "string",
          "description": "A sentence that works as a meta description. Max 140 characters. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "teamSize": {
          "type": "string",
          "description": "How many people work on it, as a number or a range like 2-10. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "coverFile": {
          "type": "string",
          "description": "The same cover as a file on the machine your agent runs on, for the forms that upload it."
        },
        "makerName": {
          "type": "string",
          "description": "The person shown as the maker, first and last name, exactly as it should be printed on a listing. Launch boards and founder profiles require it. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "makerRole": {
          "type": "string",
          "description": "Their title, like Founder or Solo developer. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "techStack": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "What it is built with, five at most, like Next.js or Postgres. Launch boards for builders make it a required step. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "buildStory": {
          "type": "string",
          "description": "Why and how it was built, with the real numbers, for community posts and maker showcases that want a story rather than a description. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "categories": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Three at most, in the platform's own vocabulary where it has one. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "howItWorks": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Three short steps, one per line, under 200 characters each. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "companyName": {
          "type": "string",
          "description": "The business behind the product, when it is not the product name. Review sites and B2B directories list the company. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "foundedYear": {
          "type": "string",
          "description": "Four digits. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "galleryUrls": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Three to five, first one a poster slide. Public addresses, one per line. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "contactEmail": {
          "type": "string",
          "description": "The address you submit under. Most forms ask for one, and it is where the confirmation link lands, so a mailbox nobody opens stalls the submission. Per product, not per person: a client launch and a side project rarely want the same address, and the one you use here is the one the listing belongs to. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "demoVideoUrl": {
          "type": "string",
          "description": "Optional almost everywhere, decisive on a few. A watch page or a hosted file."
        },
        "firstComment": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Product Hunt and Show HN only, and it matters there. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "fundingStage": {
          "type": "string",
          "description": "Bootstrapped, or what was raised and at which stage. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "galleryFiles": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "The same frames as files on the machine your agent runs on, in the same order, one per line. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "alternativeTo": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Well-known products this replaces, by name. Alternative directories will not take a listing without one. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "demoVideoFile": {
          "type": "string",
          "description": "The video as a file on the machine your agent runs on, for the few platforms that upload rather than embed."
        },
        "monthlyRevenue": {
          "type": "string",
          "description": "MRR or a bracket like $0-1k, and only if the maker is happy for it to be public: forms that ask for it usually display it. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "longDescription": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Two or three paragraphs, for the forms with room. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "monthlyVisitors": {
          "type": "string",
          "description": "A real figure from their analytics. Some directories make it a required number and print it. Only what the maker told you, never a guess. When a form requires it and it is empty here, ask the maker once, then save the answer with update_project so no other form stops on it. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "shortDescription": {
          "type": "string",
          "description": "The paragraph most directory forms ask for. Max 260 characters. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        },
        "extendedDescription": {
          "type": "string",
          "description": "A full write-up of 400 words or more (about 2,500 characters) for the forms that set a minimum length. The long description above is shorter on purpose. Drafting it from the product is fine, but show the maker before it is used, because it goes out under their name. Write it as the maker would: no em dashes, which read as machine-written and get the whole listing dismissed. Use a comma, a colon, a semicolon, brackets or a full stop instead."
        }
      }
    },
    "facts": {
      "type": "object"
    },
    "pitch": {
      "type": "string"
    },
    "projectId": {
      "type": "string",
      "description": "Omit it when the account has one project, which is the free tier."
    }
  }
}
submission_playbook
Advertised by the server during the last successful anonymous check.

Everything needed to submit a stored project to one platform, yourself, in the maker's browser: a preflight of what is still missing, the sign-in plan (on the first submission it carries a question for the maker: hand every login back to them, or use the Google address they write out, which you then pass back as signInAs), the pack values mapped onto the fields the form asks for, the steps, the gotchas, the agentGotchas (traps in the form itself), and the call to make afterwards. Read the preflight before opening a tab. One brief is out at a time per project: ask for the next platform only after record_submission has answered this one, because until then this tool refuses the next brief and names the one still open. Do not fetch briefs ahead or in parallel. This is the tool the free plan meters: it covers the same submissions the dashboard tracks, plus any platform the account has already sent to, and past that it says so and points at the upgrade rather than answering.

submission_playbook input schema
{
  "type": "object",
  "required": [
    "platformSlug"
  ],
  "properties": {
    "signInAs": {
      "type": "string",
      "description": "The Google address the maker wrote out when they agreed you could sign in, or sign up, for them on this project, whether they said it when asked or before you started. Pass it on every call after they say it, so the answer travels with the work instead of living in your head. It belongs to one project: on a different one, ask again rather than sending the address you were given for the last. Leave it out if they have not written one: sign-in then goes back to them, which is the safe end."
    },
    "projectId": {
      "type": "string",
      "description": "Omit it when the account has one project, which is the free tier."
    },
    "platformSlug": {
      "type": "string"
    }
  }
}
record_submission
Advertised by the server during the last successful anonymous check.

Log what happened to the dashboard: the listing URL, when it was sent, when it goes live. Call it as soon as a submission lands, before you ask for the next brief, including when it is only queued for review, and including when you are not sure it landed: that is what `attempted` is for. The next brief on this project is refused until this call is made. A platform that needs the maker (a captcha, a login, a payment, a file, an address to confirm) is recorded as `planned` with a note saying what you need from them: that hands it back without claiming an outcome, and frees the run to carry on. If the live platform did not match what the brief said, put the difference in `differsFromRecord`. This costs nothing on any plan and no status costs more than another, so record what actually happened: the brief was what spent the tracked platform, and leaving the row wrong now only makes the run harder to follow.

record_submission input schema
{
  "type": "object",
  "required": [
    "platformSlug",
    "status"
  ],
  "properties": {
    "note": {
      "type": "string",
      "description": "Required with `planned`: what you need from the maker, in one line they can act on."
    },
    "status": {
      "enum": [
        "planned",
        "attempted",
        "submitted",
        "live",
        "rejected",
        "needs_changes"
      ],
      "type": "string",
      "description": "Use `attempted` when the form was sent and you cannot prove it arrived: the tab died, the connection dropped, the page never came back. It is not a lesser `submitted`, it is the honest one, and it carries the verification the platform record describes so somebody can settle it later. Never re-send a form to find out; every platform has a way to check without submitting twice. Use `planned`, with a note, to hand a platform back to the maker when it is waiting on them. A first record is `submitted`, `attempted` or `planned`: `live`, `rejected` and `needs_changes` come after one of those."
    },
    "launchAt": {
      "type": "string",
      "description": "ISO 8601, if it is scheduled."
    },
    "projectId": {
      "type": "string",
      "description": "Omit it when the account has one project, which is the free tier."
    },
    "listingUrl": {
      "type": "string",
      "description": "The product's URL on that platform."
    },
    "submittedAt": {
      "type": "string",
      "description": "ISO 8601."
    },
    "platformSlug": {
      "type": "string"
    },
    "differsFromRecord": {
      "type": "string",
      "description": "Only when the live platform did not match the brief or the platform record: say what differed, in one line, as fact. A price where the record said free, a sign-in or phone check it did not mention, a field it did not list, a submit page that moved, an audience it does not accept. We read these and fix the record, so the next maker is not sent into the same wall. Leave it out when everything matched."
    }
  }
}
plan_submissions
Advertised by the server during the last successful anonymous check.

Write the run order for a project: which platforms, in what sequence, and why each one is where it is. Anything already submitted to is dropped from the plan and named back to you in `alreadySubmitted`, because a plan is what is still ahead. Pass the platforms in the order they should be worked, with a short reason on each, plus a summary of the strategy. The plan appears on the maker's dashboard as a checklist that ticks itself off as submissions land. Anything already tracked is reordered rather than reset. Call it after qualify_project, using the `recommended` list it hands back, and if that response carried `questions`, put them to the maker before you plan: the order depends on the answers, and a plan written on an assumption is one they have to undo by hand. A plan costs nothing on any plan, so plan the whole run the product deserves rather than a short one sized to a guess about what the account can track.

plan_submissions input schema
{
  "type": "object",
  "required": [
    "items"
  ],
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "platformSlug"
        ],
        "properties": {
          "reason": {
            "type": "string",
            "description": "Why this platform, and why here in the order."
          },
          "platformSlug": {
            "type": "string"
          }
        }
      },
      "description": "In the order they should be submitted. First is next."
    },
    "summary": {
      "type": "string",
      "description": "Why this order, in a sentence or two. The maker reads this."
    },
    "checklist": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "title"
        ],
        "properties": {
          "title": {
            "type": "string",
            "description": "Short and doable, as an instruction."
          },
          "detail": {
            "type": "string",
            "description": "The specifics: sizes, lengths, why."
          }
        }
      },
      "description": "What the maker has to prepare before any of this can be submitted: the gallery, the demo video, a privacy policy page, a launch date. Write it from what the platforms in this plan actually demand and what the project is missing, not from a generic template. The maker ticks these off or throws them away on their dashboard, and anything they have already decided on keeps that decision when you plan again."
    },
    "projectId": {
      "type": "string",
      "description": "Omit it when the account has one project, which is the free tier."
    }
  }
}
list_submissions
Advertised by the server during the last successful anonymous check.

Where every submission for a project stands: what went out, when, what came back, and what is still waiting. This is the tool behind "check my submissions", "what did I submit", "did I ever submit to that one" and "how is my launch going", so answer those from here instead of asking the maker to remember. Start with list_projects if they have not named a project.

list_submissions input schema
{
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "description": "Omit it when the account has one project, which is the free tier."
    }
  }
}

Prompts

Not advertised

No prompt definitions available.

Resources

Not advertised

No resource definitions available.

90-day uptime

A lightweight connection check about every six hours, sampled on hosts with many listings. It shows the server answered, not that its tools work.

Too few checks for a percentage yet (2 of 2 answered).

  1. 2026-11-06: 1 of 1 checks answered
  2. 2026-11-07: 1 of 1 checks answered

30-day availability

Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.

Successful checks
1 of 1 (100%)
Average latency
713 ms
95th percentile latency
713 ms

Maintenance history

Public check and source-refresh events. Editorial and ownership records remain private.

  • · probe · passed · 713 ms · metadata changed · 11 tool(s)

Connect from your client

Each snippet registers the same endpoint; pick the client you use.

This server is operated by the named third party. Listing and endpoint checks are not an endorsement or a security audit. Review the operator's permissions, terms, and data handling before connecting. How listings are checked and corrected.

Claude Code

Run this once in a terminal. It registers the remote server. Use --scope project to write .mcp.json for the repo.
Claude Code configuration
claude mcp add --transport http submitmap-mcp https://submitmap.com/api/mcp

Codex

Run this once. Codex stores MCP servers in ~/.codex/config.toml (or .codex/config.toml in a trusted project).
Codex configuration
codex mcp add submitmap-mcp --url https://submitmap.com/api/mcp

Cursor

Merge this server into .cursor/mcp.json in your project, then enable submitmap-mcp in your MCP settings.
Cursor configuration
{
  "mcpServers": {
    "submitmap-mcp": {
      "url": "https://submitmap.com/api/mcp"
    }
  }
}

Cline

Merge this into cline_mcp_settings.json (Cline → MCP Servers → Configure).
Cline configuration
{
  "mcpServers": {
    "submitmap-mcp": {
      "type": "streamableHttp",
      "url": "https://submitmap.com/api/mcp",
      "disabled": false
    }
  }
}

VS Code

Merge this server into .vscode/mcp.json. Use MCP: List Servers to start submitmap-mcp and inspect its tools.
VS Code configuration
{
  "servers": {
    "submitmap-mcp": {
      "type": "http",
      "url": "https://submitmap.com/api/mcp"
    }
  }
}

Gemini CLI

Run this once. It writes .gemini/settings.json in the current project; pass --scope user for ~/.gemini/settings.json.
Gemini CLI configuration
gemini mcp add --transport http submitmap-mcp https://submitmap.com/api/mcp

Claude Desktop

Open Claude Settings > Connectors > Add custom connector, enter this server URL and enable it for the conversation. This is a manual remote-connector handoff, not a desktop JSON configuration.
Claude Desktop configuration
https://submitmap.com/api/mcp

Windsurf

Merge this into ~/.codeium/windsurf/mcp_config.json, then refresh MCP in Cascade. Windsurf remote servers use serverUrl, not url.
Windsurf configuration
{
  "mcpServers": {
    "submitmap-mcp": {
      "serverUrl": "https://submitmap.com/api/mcp"
    }
  }
}

Let your agent find its tools

Find servers like SubmitMap MCP from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.

Do you run SubmitMap MCP?

Prove ownership with a DNS record, a well-known file, or your Official MCP Registry entry, and your edits publish immediately. Proofs are re-checked weekly; admins can still hide or remove a listing.

Claim this listing