Coverage for agentos/server/marketplace_server.py: 0%
67 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-08 12:20 +0800
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-08 12:20 +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, HTTPException, Query
21 from fastapi.middleware.cors import CORSMiddleware
22 from fastapi.responses import HTMLResponse
23 from fastapi.staticfiles import StaticFiles
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 (
101 q_lower in (s.name or "").lower()
102 or q_lower in (s.description or "").lower()
103 or any(q_lower in (t or "").lower() for t in (s.tags or []))
104 ):
105 results.append(
106 {
107 "name": s.name,
108 "version": s.version,
109 "description": s.description,
110 "tags": s.tags,
111 "category": s.category,
112 }
113 )
114 if len(results) >= limit:
115 break
116 return {"query": q, "count": len(results), "results": results}
118 @app.get("/api/skills/{name}")
119 async def get_skill(name: str):
120 """Get detailed info for a specific skill."""
121 skill = registry.get_installed(name)
122 if not skill:
123 raise HTTPException(status_code=404, detail=f"Skill '{name}' not found")
124 return {
125 "name": skill.name,
126 "version": skill.version,
127 "description": skill.description,
128 "author": skill.author,
129 "tags": skill.tags,
130 "category": skill.category,
131 "source": skill.source,
132 "format": skill.format,
133 "entrypoint": skill.entrypoint,
134 "tools": [t.to_dict() if hasattr(t, "to_dict") else t for t in (skill.tools or [])],
135 "dependencies": skill.dependencies,
136 "installed_at": getattr(skill, "installed_at", None),
137 }
139 # ── Ecosystem API ────────────────────────
141 @app.get("/api/ecosystems")
142 async def list_ecosystems():
143 """List external skill ecosystems compatible with AgentOS."""
144 return {
145 "ecosystems": [
146 {
147 "name": "OpenClaw Skill Store",
148 "url": "https://github.com/nicepkg/openclaw-skill-store",
149 "skill_count": "13,700+",
150 "format": "openclaw",
151 "description": "Largest community-driven skill ecosystem",
152 "badge": "github",
153 },
154 {
155 "name": "ClawHub",
156 "url": "https://clawhub.eu.org/",
157 "skill_count": "curated",
158 "format": "openclaw",
159 "description": "Curated high-quality OpenClaw skills",
160 },
161 {
162 "name": "Skills Marketplace",
163 "url": "https://skills.sh/",
164 "skill_count": "multi-framework",
165 "format": "openclaw/agentos",
166 "description": "Cross-framework skill discovery platform",
167 },
168 {
169 "name": "MCP Servers",
170 "url": "https://github.com/modelcontextprotocol/servers",
171 "skill_count": "2,000+",
172 "format": "mcp",
173 "description": "Official MCP server registry",
174 },
175 {
176 "name": "LobeHub Plugins",
177 "url": "https://lobehub.com/plugins",
178 "skill_count": "356+",
179 "format": "lobehub",
180 "description": "LobeChat plugin ecosystem, adaptable to AgentOS",
181 },
182 {
183 "name": "Awesome Agent Skills",
184 "url": "https://github.com/topics/agent-skills",
185 "skill_count": "curated",
186 "format": "multi",
187 "description": "Community curated list of agent skills",
188 },
189 ]
190 }
192 # ── MCP Info API ─────────────────────────
194 @app.get("/api/mcp/servers")
195 async def mcp_servers():
196 """List built-in MCP servers and their tools."""
197 try:
198 from agentos.mcp.builtin_servers import create_default_registry
200 reg = create_default_registry()
201 return {
202 "servers": [
203 {
204 "name": name,
205 "tool_count": len(reg._servers[name].get_tools()),
206 "tools": [
207 {"name": t["name"], "description": t["description"]}
208 for t in reg._servers[name].get_tools()
209 ],
210 }
211 for name in reg.server_names
212 ],
213 "total_tools": reg.tool_count,
214 }
215 except Exception as e:
216 return {"error": str(e), "servers": []}
218 return app
221def start_marketplace_server(host: str = "0.0.0.0", port: int = 8910) -> None:
222 """Start the marketplace server (blocking)."""
223 import uvicorn
225 app = create_marketplace_app()
226 print("\n AgentOS Skill Marketplace")
227 print(f" Local: http://{host}:{port}")
228 print(f" Skills: {len(SkillRegistry().list_installed())} installed")
229 print()
230 uvicorn.run(app, host=host, port=port, log_level="info")