Phase 4 distribution audit: server-card.json + auth-wall findings
- Add /.well-known/mcp/server-card.json (Smithery auto-scan endpoint) - Sitemap.xml now lists both well-known endpoints - JOURNAL.md: full audit of marketplace auth walls (mcpservers.org=$39, mcp.so/smithery/glama=login) - JAOUAD_TODO.md: updated with concrete copy-paste instructions for the 3 directories - STATE.md: phase 4 marked blocked by auth walls until human steps in - deploy/inspect_*.py + submit_mcpservers.py: playwright probes (kept for re-runs)
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
"""Inspect glama.ai Add Server flow."""
|
||||
from playwright.sync_api import sync_playwright
|
||||
from pathlib import Path
|
||||
|
||||
with sync_playwright() as p:
|
||||
b = p.chromium.launch(headless=True)
|
||||
ctx = b.new_context(user_agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36")
|
||||
page = ctx.new_page()
|
||||
page.goto("https://glama.ai/mcp/servers", wait_until="domcontentloaded", timeout=30000)
|
||||
# find Add Server link
|
||||
link = page.query_selector('a:has-text("Add Server")')
|
||||
if link:
|
||||
href = link.get_attribute("href")
|
||||
print("ADD SERVER HREF:", href)
|
||||
if href.startswith("/"):
|
||||
href = "https://glama.ai" + href
|
||||
page.goto(href, wait_until="domcontentloaded", timeout=30000)
|
||||
print("--- ADD PAGE ---")
|
||||
print("URL:", page.url)
|
||||
print("TITLE:", page.title())
|
||||
print(page.inner_text("body")[:1500])
|
||||
Path("data/glama_add.html").write_text(page.content())
|
||||
# any forms?
|
||||
for inp in page.query_selector_all("input"):
|
||||
print(" INPUT:", inp.get_attribute("name"), inp.get_attribute("type"), inp.get_attribute("placeholder"))
|
||||
else:
|
||||
print("No Add Server link found")
|
||||
b.close()
|
||||
@@ -0,0 +1,21 @@
|
||||
"""Inspect mcp.so submission flow."""
|
||||
from playwright.sync_api import sync_playwright
|
||||
from pathlib import Path
|
||||
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "mcpso.html"
|
||||
|
||||
with sync_playwright() as p:
|
||||
b = p.chromium.launch(headless=True)
|
||||
ctx = b.new_context(user_agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36")
|
||||
page = ctx.new_page()
|
||||
for url in ("https://mcp.so/submit", "https://mcp.so/", "https://glama.ai/mcp/servers"):
|
||||
try:
|
||||
page.goto(url, wait_until="domcontentloaded", timeout=30000)
|
||||
print(f"--- {url} -> {page.url}")
|
||||
print("TITLE:", page.title())
|
||||
txt = page.inner_text("body")[:600]
|
||||
print(txt)
|
||||
print()
|
||||
except Exception as e:
|
||||
print(f"FAIL {url}: {e}")
|
||||
b.close()
|
||||
@@ -0,0 +1,29 @@
|
||||
"""Inspect smithery.ai/new submission form."""
|
||||
from playwright.sync_api import sync_playwright
|
||||
from pathlib import Path
|
||||
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "smithery_new.html"
|
||||
|
||||
with sync_playwright() as p:
|
||||
b = p.chromium.launch(headless=True)
|
||||
ctx = b.new_context(user_agent="Mozilla/5.0 Aegis402-bot/0.1")
|
||||
page = ctx.new_page()
|
||||
page.goto("https://smithery.ai/new", wait_until="networkidle", timeout=45000)
|
||||
OUT.write_text(page.content())
|
||||
print("URL:", page.url)
|
||||
print("TITLE:", page.title())
|
||||
# capture all forms / inputs
|
||||
inputs = page.query_selector_all("input")
|
||||
print(f"INPUTS: {len(inputs)}")
|
||||
for i in inputs:
|
||||
print(" -", i.get_attribute("name"), i.get_attribute("type"), i.get_attribute("placeholder"))
|
||||
btns = page.query_selector_all("button")
|
||||
print(f"BUTTONS: {len(btns)}")
|
||||
for bt in btns:
|
||||
t = bt.inner_text().strip()
|
||||
if t:
|
||||
print(" -", t)
|
||||
# body text first 1500 chars
|
||||
print("---BODY---")
|
||||
print(page.inner_text("body")[:1500])
|
||||
b.close()
|
||||
@@ -0,0 +1,102 @@
|
||||
"""Submit Aegis402 to mcpservers.org via Playwright headless chromium.
|
||||
|
||||
Form spec (inspected previously):
|
||||
GET https://mcpservers.org/submit
|
||||
fields: name, description, url, category(select), email, terms(checkbox)
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "mcpservers_submit.html"
|
||||
OUT.parent.mkdir(exist_ok=True)
|
||||
|
||||
PAYLOAD = {
|
||||
"name": "Aegis402",
|
||||
"description": "Pay-per-call CVE intel for AI agent dependencies — scans GHSA + CISA KEV, x402 native, USDC on Base, no signup.",
|
||||
"url": "https://aegis402.vmaxbadge.ch/",
|
||||
"email": "contact@vmaxbadge.ch",
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(headless=True)
|
||||
ctx = browser.new_context(user_agent="Mozilla/5.0 Aegis402-bot/0.1 (+https://aegis402.vmaxbadge.ch/)")
|
||||
page = ctx.new_page()
|
||||
try:
|
||||
page.goto("https://mcpservers.org/submit", wait_until="networkidle", timeout=30000)
|
||||
except Exception as e:
|
||||
print(f"FAIL goto: {e}")
|
||||
return 2
|
||||
|
||||
try:
|
||||
page.fill('input[name="name"]', PAYLOAD["name"])
|
||||
page.fill('input[name="description"], textarea[name="description"]', PAYLOAD["description"])
|
||||
page.fill('input[name="url"]', PAYLOAD["url"])
|
||||
page.fill('input[name="email"]', PAYLOAD["email"])
|
||||
except Exception as e:
|
||||
print(f"FAIL fill: {e}")
|
||||
OUT.write_text(page.content())
|
||||
return 3
|
||||
|
||||
# category select — try Security first, fallback Development
|
||||
try:
|
||||
sel = page.query_selector('select[name="category"]')
|
||||
if sel:
|
||||
opts = [o.inner_text().strip() for o in sel.query_selector_all("option")]
|
||||
print("CATEGORY OPTIONS:", opts)
|
||||
pick = None
|
||||
for cand in ("Security", "Developer Tools", "Development", "Tools"):
|
||||
for o in opts:
|
||||
if cand.lower() in o.lower():
|
||||
pick = o
|
||||
break
|
||||
if pick:
|
||||
break
|
||||
if pick:
|
||||
page.select_option('select[name="category"]', label=pick)
|
||||
print("PICKED:", pick)
|
||||
except Exception as e:
|
||||
print(f"WARN category: {e}")
|
||||
|
||||
# checkbox(es)
|
||||
try:
|
||||
for cb in page.query_selector_all('input[type="checkbox"]'):
|
||||
if not cb.is_checked():
|
||||
cb.check()
|
||||
except Exception as e:
|
||||
print(f"WARN checkbox: {e}")
|
||||
|
||||
OUT.write_text(page.content())
|
||||
print(f"PRE-SUBMIT html saved to {OUT}")
|
||||
|
||||
# find submit button
|
||||
try:
|
||||
btn = page.query_selector('button[type="submit"], input[type="submit"]')
|
||||
if not btn:
|
||||
print("FAIL: no submit button found")
|
||||
return 4
|
||||
btn.click()
|
||||
page.wait_for_load_state("networkidle", timeout=20000)
|
||||
except Exception as e:
|
||||
print(f"FAIL submit: {e}")
|
||||
OUT.write_text(page.content())
|
||||
return 5
|
||||
|
||||
post = Path(__file__).resolve().parent.parent / "data" / "mcpservers_response.html"
|
||||
post.write_text(page.content())
|
||||
print(f"POST-SUBMIT html: {post}")
|
||||
print(f"FINAL URL: {page.url}")
|
||||
# quick success heuristic
|
||||
text = page.inner_text("body").lower()
|
||||
for kw in ("thank", "received", "submitted", "success", "review"):
|
||||
if kw in text:
|
||||
print(f"SUCCESS keyword found: {kw}")
|
||||
return 0
|
||||
print("NO success keyword — inspect HTML manually")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user