Coverage for agentos/server/marketplace_server.py: 0%
67 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-06 10:59 +0800
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-06 10:59 +0800
1"""
2AgentOS Skill Marketplace Server (v1.8.1).
4FastAPI server serving:
5 - /api/skills/installed — list all 64+ installed skills
6 - /api/skills/search — search by name/description/tag
7 - /api/skills/{name} — get skill detail
8 - /api/ecosystems — external ecosystem links
9 - / — marketplace web UI (static page)
11Also serves built-in MCP info: /api/mcp/servers, /api/mcp/tools
12"""
14from __future__ import annotations
16from pathlib import Path
18# ── FastAPI app ──
19try:
20 from fastapi import FastAPI, Query, HTTPException
21 from fastapi.responses import HTMLResponse, JSONResponse
22 from fastapi.staticfiles import StaticFiles
23 from fastapi.middleware.cors import CORSMiddleware
24except ImportError:
25 FastAPI = object # type: ignore
27from agentos.marketplace.registry import SkillRegistry
29STATIC_DIR = Path(__file__).parent / "static"
32def create_marketplace_app() -> "FastAPI":
33 """Create and configure the marketplace FastAPI application."""
35 if FastAPI is object:
36 raise RuntimeError("FastAPI not installed. Run: pip install fastapi uvicorn")
38 app = FastAPI(
39 title="AgentOS Skill Marketplace",
40 version="1.8.1",
41 description="Browse, search, and install skills. Compatible with OpenClaw/MCP ecosystem.",
42 )
44 app.add_middleware(
45 CORSMiddleware,
46 allow_origins=["*"],
47 allow_credentials=True,
48 allow_methods=["*"],
49 allow_headers=["*"],
50 )
52 registry = SkillRegistry()
54 # ── Static files ─────────────────────────
56 if STATIC_DIR.exists():
57 app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
59 @app.get("/", response_class=HTMLResponse)
60 async def index():
61 """Serve marketplace web UI."""
62 html_path = STATIC_DIR / "marketplace.html"
63 if html_path.exists():
64 content = html_path.read_text(encoding="utf-8")
65 return HTMLResponse(content)
66 return HTMLResponse("<h1>Marketplace UI not found</h1>", status_code=404)
68 # ── Skill APIs ───────────────────────────
70 @app.get("/api/skills/installed")
71 async def list_installed():
72 """List all installed skills with metadata."""
73 skills = registry.list_installed()
74 return {
75 "count": len(skills),
76 "skills": [
77 {
78 "name": s.name,
79 "version": s.version,
80 "description": s.description,
81 "author": s.author,
82 "tags": s.tags,
83 "category": s.category,
84 "source": s.source,
85 "format": s.format,
86 "entrypoint": s.entrypoint,
87 "installed_at": getattr(s, "installed_at", None),
88 }
89 for s in skills
90 ],
91 }
93 @app.get("/api/skills/search")
94 async def search_skills(q: str = Query("", description="Search query"), limit: int = Query(50)):
95 """Search installed skills by name/description/tag."""
96 skills = registry.list_installed()
97 q_lower = q.lower()
98 results = []
99 for s in skills:
100 if (q_lower in (s.name or "").lower() or
101 q_lower in (s.description or "").lower() or
102 any(q_lower in (t or "").lower() for t in (s.tags or []))):
103 results.append({
104 "name": s.name, "version": s.version, "description": s.description,
105 "tags": s.tags, "category": s.category,
106 })
107 if len(results) >= limit:
108 break
109 return {"query": q, "count": len(results), "results": results}
111 @app.get("/api/skills/{name}")
112 async def get_skill(name: str):
113 """Get detailed info for a specific skill."""
114 skill = registry.get_installed(name)
115 if not skill:
116 raise HTTPException(status_code=404, detail=f"Skill '{name}' not found")
117 return {
118 "name": skill.name,
119 "version": skill.version,
120 "description": skill.description,
121 "author": skill.author,
122 "tags": skill.tags,
123 "category": skill.category,
124 "source": skill.source,
125 "format": skill.format,
126 "entrypoint": skill.entrypoint,
127 "tools": [t.to_dict() if hasattr(t, "to_dict") else t for t in (skill.tools or [])],
128 "dependencies": skill.dependencies,
129 "installed_at": getattr(skill, "installed_at", None),
130 }
132 # ── Ecosystem API ────────────────────────
134 @app.get("/api/ecosystems")
135 async def list_ecosystems():
136 """List external skill ecosystems compatible with AgentOS."""
137 return {
138 "ecosystems": [
139 {
140 "name": "OpenClaw Skill Store",
141 "url": "https://github.com/nicepkg/openclaw-skill-store",
142 "skill_count": "13,700+",
143 "format": "openclaw",
144 "description": "Largest community-driven skill ecosystem",
145 "badge": "github",
146 },
147 {
148 "name": "ClawHub",
149 "url": "https://clawhub.eu.org/",
150 "skill_count": "curated",
151 "format": "openclaw",
152 "description": "Curated high-quality OpenClaw skills",
153 },
154 {
155 "name": "Skills Marketplace",
156 "url": "https://skills.sh/",
157 "skill_count": "multi-framework",
158 "format": "openclaw/agentos",
159 "description": "Cross-framework skill discovery platform",
160 },
161 {
162 "name": "MCP Servers",
163 "url": "https://github.com/modelcontextprotocol/servers",
164 "skill_count": "2,000+",
165 "format": "mcp",
166 "description": "Official MCP server registry",
167 },
168 {
169 "name": "LobeHub Plugins",
170 "url": "https://lobehub.com/plugins",
171 "skill_count": "356+",
172 "format": "lobehub",
173 "description": "LobeChat plugin ecosystem, adaptable to AgentOS",
174 },
175 {
176 "name": "Awesome Agent Skills",
177 "url": "https://github.com/topics/agent-skills",
178 "skill_count": "curated",
179 "format": "multi",
180 "description": "Community curated list of agent skills",
181 },
182 ]
183 }
185 # ── MCP Info API ─────────────────────────
187 @app.get("/api/mcp/servers")
188 async def mcp_servers():
189 """List built-in MCP servers and their tools."""
190 try:
191 from agentos.mcp.builtin_servers import create_default_registry
192 reg = create_default_registry()
193 return {
194 "servers": [
195 {
196 "name": name,
197 "tool_count": len(reg._servers[name].get_tools()),
198 "tools": [
199 {"name": t["name"], "description": t["description"]}
200 for t in reg._servers[name].get_tools()
201 ],
202 }
203 for name in reg.server_names
204 ],
205 "total_tools": reg.tool_count,
206 }
207 except Exception as e:
208 return {"error": str(e), "servers": []}
210 return app
213def start_marketplace_server(host: str = "0.0.0.0", port: int = 8910) -> None:
214 """Start the marketplace server (blocking)."""
215 import uvicorn
216 app = create_marketplace_app()
217 print("\n AgentOS Skill Marketplace")
218 print(f" Local: http://{host}:{port}")
219 print(f" Skills: {len(SkillRegistry().list_installed())} installed")
220 print()
221 uvicorn.run(app, host=host, port=port, log_level="info")