diff --git a/examples/youcom-web-search/.env.example b/examples/youcom-web-search/.env.example new file mode 100644 index 000000000..eee124cb3 --- /dev/null +++ b/examples/youcom-web-search/.env.example @@ -0,0 +1,4 @@ +# Optional You.com API key for higher quotas and enhanced features +# Without this, the tool uses keyless operation (100 free searches/day) +# Get your API key at: https://you.com/platform/api-keys +YDC_API_KEY=your-api-key-here diff --git a/examples/youcom-web-search/.gitignore b/examples/youcom-web-search/.gitignore new file mode 100644 index 000000000..ecff56526 --- /dev/null +++ b/examples/youcom-web-search/.gitignore @@ -0,0 +1,3 @@ +.env +node_modules +dist diff --git a/examples/youcom-web-search/README.md b/examples/youcom-web-search/README.md new file mode 100644 index 000000000..afafc281e --- /dev/null +++ b/examples/youcom-web-search/README.md @@ -0,0 +1,172 @@ +# You.com Web Search MCP Tool + +This example demonstrates how to integrate You.com's web search capabilities into a Skybridge MCP app, providing real-time web search functionality with rich interactive results. + +## Features + +- **Web search**: Search the web using You.com's powerful search engine +- **Keyless operation**: Works without API key (100 free searches per day) +- **Enhanced features**: Higher quotas and additional features with optional API key +- **Rich UI**: Interactive search results with titles, snippets, and source information +- **Search options**: Support for domain filtering, freshness filtering, and safe search +- **Real-time search**: Interactive search interface within the MCP app +- **Error handling**: Graceful handling of rate limits, API errors, and network issues + +## Setup + +### Quick Start (Keyless Mode) + +No setup required! The tool works immediately with 100 free searches per day: + +```bash +npm install +npm run dev +``` + +### Enhanced Mode (With API Key) + +For higher quotas and enhanced features: + +1. Get your API key at [you.com/platform/api-keys](https://you.com/platform/api-keys) +2. Create `.env` file: + ``` + YDC_API_KEY=your-api-key-here + ``` +3. Start the development server: + ```bash + npm run dev + ``` + +## Usage + +### MCP Tool + +The example registers a `youcom-search` tool with the following parameters: + +- `query` (required): Search query string +- `count` (optional): Number of results (1-20, default: 10) +- `domains` (optional): Array of domains to restrict search to +- `freshness` (optional): Filter by content age ("hour", "day", "week", "month", "year") +- `safeSearch` (optional): Enable safe search filtering (default: true) + +### Example Tool Calls + +```typescript +// Basic search +await callTool("youcom-search", { + query: "TypeScript MCP frameworks" +}); + +// Advanced search with filters +await callTool("youcom-search", { + query: "React hooks patterns", + count: 15, + domains: ["reactjs.org", "github.com"], + freshness: "month", + safeSearch: true +}); +``` + +### Interactive UI + +The app provides a rich search interface with: + +- Real-time search input +- Visual result cards with titles, snippets, and favicons +- Source domain display and external link indicators +- Loading states and error handling +- Search options display (keyless mode, domain filters, etc.) +- Responsive design that works across devices + +## API Integration + +The integration uses You.com's Search API: + +- **Endpoint**: `https://api.you.com/v1/agents/search` +- **Authentication**: Optional Bearer token (`YDC_API_KEY`) +- **Rate limits**: + - Keyless: 100 searches/day per IP + - With API key: Higher quotas based on plan +- **Response format**: Structured JSON with web and news results + +### Error Handling + +The tool gracefully handles: + +- **401 Unauthorized**: Invalid API key guidance +- **429 Rate Limited**: Clear messaging about quota limits with upgrade suggestions +- **5xx Server Errors**: Service availability notifications +- **Network errors**: Connection issue messaging +- **Malformed responses**: Data validation and fallbacks + +## Implementation Details + +### Architecture + +``` +src/ +├── youcom-client.ts # You.com API client with error handling +├── server.ts # MCP server with tool registration +├── helpers.ts # Type-safe tool calling helpers +├── env.ts # Environment configuration +└── views/ + └── youcom-search-results/ + └── index.tsx # React search results UI +``` + +### Key Components + +1. **YouComSearchClient**: Handles API communication, authentication, and error handling +2. **MCP Tool Registration**: Defines the tool schema and implementation +3. **React UI Component**: Interactive search interface with real-time updates +4. **Type Safety**: Full TypeScript support with proper type inference + +### Integration Patterns + +The example follows Skybridge's established patterns: + +- Uses `McpServer.registerTool()` for tool definition +- Implements structured content for model consumption +- Provides rich UI views for human interaction +- Includes proper error handling and user feedback +- Supports both keyless and authenticated operation modes + +## Development + +```bash +# Install dependencies +npm install + +# Development with hot reload +npm run dev + +# Development with tunnel (for ChatGPT/Claude testing) +npm run dev:tunnel + +# Build for production +npm run build + +# Start production server +npm start +``` + +## Integration with AI Assistants + +This MCP tool works seamlessly with: + +- **Claude Code**: Install via plugin marketplace +- **ChatGPT**: Deploy as MCP app +- **VSCode Extensions**: Via MCP protocol +- **Any MCP Client**: Standard MCP tool interface + +Ask your AI assistant to search for information: + +> "Use the youcom-search tool to find recent TypeScript best practices" + +> "Search for React 19 new features from the last month" + +> "Find documentation about MCP servers on GitHub" + +## License + +MIT - see the main Skybridge repository for details. diff --git a/examples/youcom-web-search/package.json b/examples/youcom-web-search/package.json new file mode 100644 index 000000000..f892ae090 --- /dev/null +++ b/examples/youcom-web-search/package.json @@ -0,0 +1,42 @@ +{ + "name": "skybridge-youcom-web-search-example", + "version": "0.0.1", + "private": true, + "description": "You.com Web Search MCP Tool Example", + "type": "module", + "scripts": { + "dev": "skybridge dev", + "dev:tunnel": "skybridge dev --tunnel", + "build": "skybridge build", + "start": "skybridge start" + }, + "dependencies": { + "@alpic-ai/insights": "^1.142.1", + "@modelcontextprotocol/sdk": "^1.29.0", + "@t3-oss/env-core": "^0.13.11", + "clsx": "^2.1.1", + "dotenv": "^17.4.2", + "express": "^5.2.1", + "lucide-react": "^0.562.0", + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-router-dom": "^7.18.0", + "skybridge": "^1.1.0", + "tailwind-merge": "^3.6.0", + "tailwindcss": "^4.3.1", + "zod": "^4.4.3" + }, + "devDependencies": { + "@skybridge/devtools": "^1.2.3", + "@tailwindcss/vite": "^4.3.1", + "@types/express": "^5.0.6", + "@types/node": "^22.20.0", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.2", + "tsx": "^4.22.4", + "tw-animate-css": "^1.4.0", + "typescript": "^5.9.3", + "vite": "^8.1.5" + } +} diff --git a/examples/youcom-web-search/src/env.ts b/examples/youcom-web-search/src/env.ts new file mode 100644 index 000000000..1c0a08c80 --- /dev/null +++ b/examples/youcom-web-search/src/env.ts @@ -0,0 +1,13 @@ +import "dotenv/config"; + +import { createEnv } from "@t3-oss/env-core"; +import { z } from "zod"; + +export const env = createEnv({ + server: { + NODE_ENV: z.enum(["development", "production"]).default("development"), + YDC_API_KEY: z.string().optional(), + }, + runtimeEnv: process.env, + emptyStringAsUndefined: true, +}); diff --git a/examples/youcom-web-search/src/helpers.ts b/examples/youcom-web-search/src/helpers.ts new file mode 100644 index 000000000..9fb4d6fa0 --- /dev/null +++ b/examples/youcom-web-search/src/helpers.ts @@ -0,0 +1,4 @@ +import { generateHelpers } from "skybridge/web"; +import type { AppType } from "./server.js"; + +export const { useCallTool, useToolInfo } = generateHelpers(); diff --git a/examples/youcom-web-search/src/index.css b/examples/youcom-web-search/src/index.css new file mode 100644 index 000000000..ebeb83663 --- /dev/null +++ b/examples/youcom-web-search/src/index.css @@ -0,0 +1,96 @@ +@import "tailwindcss"; +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); + +@theme inline { + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); +} + +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} + +/* Utility classes for result cards */ +.line-clamp-2 { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.line-clamp-3 { + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; +} diff --git a/examples/youcom-web-search/src/server.ts b/examples/youcom-web-search/src/server.ts new file mode 100644 index 000000000..133b22280 --- /dev/null +++ b/examples/youcom-web-search/src/server.ts @@ -0,0 +1,148 @@ +import { intentMiddleware } from "@alpic-ai/insights"; +import { McpServer } from "skybridge/server"; +import * as z from "zod"; +import { YouComSearchClient, type YouComSearchOptions } from "./youcom-client.js"; + +const client = new YouComSearchClient(); + +const server = new McpServer( + { + name: "youcom-web-search", + version: "0.0.1", + }, + { capabilities: {} }, +) + .mcpMiddleware(intentMiddleware()) + .registerTool( + { + name: "youcom-search", + description: + "Search the web using You.com for current information, news, and research. Returns structured results with titles, URLs, snippets, and source information. Supports both keyless operation (100 free searches/day) and authenticated mode with higher quotas when YDC_API_KEY is provided.", + inputSchema: { + query: z + .string() + .min(1) + .describe("Search query to find information on the web"), + count: z + .number() + .min(1) + .max(20) + .optional() + .default(10) + .describe("Number of search results to return (1-20, default: 10)"), + domains: z + .array(z.string()) + .optional() + .describe("Limit search to specific domains (e.g., ['github.com', 'stackoverflow.com'])"), + freshness: z + .enum(["hour", "day", "week", "month", "year"]) + .optional() + .describe("Filter by content freshness (hour, day, week, month, year)"), + safeSearch: z + .boolean() + .optional() + .default(true) + .describe("Enable safe search filtering (default: true)"), + }, + annotations: { + readOnlyHint: true, + openWorldHint: true, + destructiveHint: false, + }, + view: { + component: "youcom-search-results", + description: "You.com web search results with rich formatting", + csp: { + resourceDomains: [ + "https://via.placeholder.com", + "https://www.google.com/s2/favicons", + ], + }, + }, + _meta: { + "openai/widgetAccessible": true, + }, + }, + async ({ query, count = 10, domains, freshness, safeSearch = true }) => { + try { + const searchOptions: YouComSearchOptions = { + query, + count, + domains, + freshness, + safeSearch, + }; + + const searchResponse = await client.search(searchOptions); + const allResults = [ + ...(searchResponse.results.web || []), + ...(searchResponse.results.news || []), + ]; + + return { + _meta: { + searchMeta: searchResponse.searchMeta, + resultCount: allResults.length, + keylessMode: !client.hasApiKey, + }, + structuredContent: { + query: searchResponse.query, + results: allResults, + searchOptions, + }, + content: [ + { + type: "text", + text: formatResultsForModel(searchResponse, allResults), + }, + ], + isError: false, + }; + } catch (error) { + const message = error instanceof Error ? error.message : "Search failed"; + + return { + _meta: { + error: message, + keylessMode: !client.hasApiKey, + }, + structuredContent: { + error: message, + query, + }, + content: [ + { + type: "text", + text: `Search failed: ${message}`, + }, + ], + isError: true, + }; + } + }, + ); + +function formatResultsForModel(searchResponse: any, results: any[]): string { + const parts = [ + `Found ${results.length} results for "${searchResponse.query}":`, + ]; + + results.forEach((result, index) => { + parts.push( + `${index + 1}. ${result.title}`, + ` URL: ${result.url}`, + ` ${result.snippet}`, + ` Source: ${result.domain || "Unknown"}`, + "" + ); + }); + + if (results.length === 0) { + parts.push("No results found. Try a different search query."); + } + + return parts.join("\n"); +} + +export default await server.run(); +export type AppType = typeof server; diff --git a/examples/youcom-web-search/src/views/youcom-search-results/index.tsx b/examples/youcom-web-search/src/views/youcom-search-results/index.tsx new file mode 100644 index 000000000..676efe82e --- /dev/null +++ b/examples/youcom-web-search/src/views/youcom-search-results/index.tsx @@ -0,0 +1,216 @@ +import { ExternalLinkIcon, SearchIcon, ClockIcon, ShieldCheckIcon } from "lucide-react"; +import React, { useState } from "react"; +import type { YouComSearchResult } from "../../youcom-client.js"; +import { useCallTool } from "../../helpers.js"; + +interface SearchResultsProps { + query: string; + results: YouComSearchResult[]; + searchOptions?: { + count?: number; + domains?: string[]; + freshness?: "hour" | "day" | "week" | "month" | "year"; + safeSearch?: boolean; + }; + searchMeta?: { + totalResults?: number; + searchTime?: string; + }; + keylessMode?: boolean; +} + +export default function YouComSearchResults({ + query: initialQuery, + results: initialResults, + searchOptions = {}, + searchMeta, + keylessMode +}: SearchResultsProps) { + const [query, setQuery] = useState(initialQuery || ""); + const [results, setResults] = useState(initialResults || []); + const { data, isPending, callTool } = useCallTool("youcom-search"); + + const handleSearch = async () => { + if (!query.trim()) return; + + callTool({ + query: query.trim(), + count: searchOptions.count || 10, + domains: searchOptions.domains, + freshness: searchOptions.freshness, + safeSearch: searchOptions.safeSearch !== undefined ? searchOptions.safeSearch : true, + }); + }; + + // Update results when data changes + React.useEffect(() => { + if (data?.structuredContent?.results) { + setResults(data.structuredContent.results); + } + }, [data]); + + const handleKeyPress = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + handleSearch(); + } + }; + + return ( +
+ {/* Header */} +
+
+ + You.com Web Search +
+

+ Powered by You.com • {keylessMode ? "Keyless Mode" : "Authenticated"} +

+
+ + {/* Search Bar */} +
+ setQuery(e.target.value)} + onKeyPress={handleKeyPress} + placeholder="Search the web..." + className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" + disabled={isPending} + /> + +
+ + {/* Search Options Display */} + {(searchOptions.domains || searchOptions.freshness || keylessMode) && ( +
+ {keylessMode && ( + + + Free Mode (100 searches/day) + + )} + {searchOptions.domains && ( + + Domains: {searchOptions.domains.join(", ")} + + )} + {searchOptions.freshness && ( + + + {searchOptions.freshness} + + )} +
+ )} + + {/* Search Meta */} + {searchMeta && ( +
+ {searchMeta.totalResults && `About ${searchMeta.totalResults.toLocaleString()} results`} + {searchMeta.searchTime && ` in ${searchMeta.searchTime}`} +
+ )} + + {/* Results */} + {results.length > 0 ? ( +
+ {results.map((result, index) => ( + + ))} +
+ ) : ( +
+ +

No results found. Try a different search query.

+
+ )} + + {/* Footer */} +
+

+ Search powered by{" "} + + You.com + + {keylessMode && ( + <> + {" • "} + + Get API key for higher quotas + + + )} +

+
+
+ ); +} + +interface SearchResultCardProps { + result: YouComSearchResult; + index: number; +} + +function SearchResultCard({ result, index }: SearchResultCardProps) { + const handleClick = () => { + window.open(result.url, "_blank", "noopener,noreferrer"); + }; + + return ( +
+
+
+ {index + 1} +
+
+
+ {result.favicon && ( + { + const target = e.target as HTMLImageElement; + target.style.display = "none"; + }} + /> + )} + {result.domain} + +
+

+ {result.title} +

+

{result.snippet}

+
{result.url}
+
+
+
+ ); +} diff --git a/examples/youcom-web-search/src/youcom-client.ts b/examples/youcom-web-search/src/youcom-client.ts new file mode 100644 index 000000000..6f8c8aa76 --- /dev/null +++ b/examples/youcom-web-search/src/youcom-client.ts @@ -0,0 +1,146 @@ +import { env } from "./env.js"; + +export interface YouComSearchResult { + title: string; + url: string; + snippet: string; + favicon?: string; + domain?: string; +} + +export interface YouComSearchResponse { + results: { + web?: YouComSearchResult[]; + news?: YouComSearchResult[]; + }; + query: string; + searchMeta?: { + totalResults?: number; + searchTime?: string; + }; +} + +export interface YouComSearchOptions { + query: string; + count?: number; + domains?: string[]; + freshness?: string; + safeSearch?: boolean; +} + +export class YouComSearchClient { + private readonly baseUrl = "https://api.you.com/v1/agents/search"; + private readonly apiKey?: string; + + constructor() { + this.apiKey = env.YDC_API_KEY; + } + + // Public getter for checking if API key is available + get hasApiKey(): boolean { + return this.apiKey !== undefined && this.apiKey.trim() !== ""; + } + + async search(options: YouComSearchOptions): Promise { + const { query, count = 10, domains, freshness, safeSearch } = options; + + const searchParams = new URLSearchParams({ + query, + count: count.toString(), + }); + + if (domains && domains.length > 0) { + searchParams.set("domains", domains.join(",")); + } + + if (freshness) { + searchParams.set("freshness", freshness); + } + + if (safeSearch !== undefined) { + searchParams.set("safesearch", safeSearch.toString()); + } + + const url = `${this.baseUrl}?${searchParams.toString()}`; + + const headers: Record = { + "Accept": "application/json", + "User-Agent": "Skybridge-YouCom-Integration/1.0", + }; + + // Add API key if available for authenticated requests + if (this.hasApiKey) { + headers["Authorization"] = `Bearer ${this.apiKey}`; + } + + try { + const response = await fetch(url, { + method: "GET", + headers, + }); + + if (!response.ok) { + // Handle specific error cases + if (response.status === 401) { + throw new Error("Invalid You.com API key. Check your YDC_API_KEY environment variable."); + } else if (response.status === 429) { + const message = this.hasApiKey + ? "You.com API rate limit exceeded. Please try again later." + : "You.com rate limit exceeded. Consider setting YDC_API_KEY for higher quotas."; + throw new Error(message); + } else if (response.status >= 500) { + throw new Error("You.com service is temporarily unavailable. Please try again later."); + } else { + throw new Error(`Search failed: ${response.status} ${response.statusText}`); + } + } + + const data = await response.json(); + + // Validate and format response + return this.formatResponse(data, query); + } catch (error) { + if (error instanceof Error) { + throw error; + } + throw new Error("Unexpected error occurred while searching"); + } + } + + private formatResponse(data: any, query: string): YouComSearchResponse { + // Handle both direct results and nested results structure + const results = data.results || data; + + return { + query, + results: { + web: this.formatResults(results.web || results.results || []), + news: this.formatResults(results.news || []), + }, + searchMeta: { + totalResults: data.searchMeta?.totalResults || results.web?.length || 0, + searchTime: data.searchMeta?.searchTime, + }, + }; + } + + private formatResults(results: any[]): YouComSearchResult[] { + if (!Array.isArray(results)) return []; + + return results.map((result) => ({ + title: result.title || result.name || "Untitled", + url: result.url || result.link || "", + snippet: result.snippet || result.description || "", + favicon: result.favicon, + domain: result.domain || this.extractDomain(result.url || result.link || ""), + })); + } + + private extractDomain(url: string): string { + try { + return new URL(url).hostname; + } catch { + return ""; + } + } +} diff --git a/examples/youcom-web-search/tsconfig.json b/examples/youcom-web-search/tsconfig.json new file mode 100644 index 000000000..0b2c66f70 --- /dev/null +++ b/examples/youcom-web-search/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "skybridge/tsconfig", + + "compilerOptions": { + "paths": { + "@/*": ["./src/*"] + } + }, + + "include": ["src", ".skybridge/**/*.d.ts"] +} diff --git a/examples/youcom-web-search/vite.config.js b/examples/youcom-web-search/vite.config.js new file mode 100644 index 000000000..1f64b975a --- /dev/null +++ b/examples/youcom-web-search/vite.config.js @@ -0,0 +1,8 @@ +import tailwindcss from "@tailwindcss/vite"; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; +import { skybridge } from "skybridge/vite"; +export default defineConfig({ + plugins: [react(), tailwindcss(), skybridge()], +}); +//# sourceMappingURL=vite.config.js.map \ No newline at end of file diff --git a/examples/youcom-web-search/vite.config.js.map b/examples/youcom-web-search/vite.config.js.map new file mode 100644 index 000000000..6e790ba9d --- /dev/null +++ b/examples/youcom-web-search/vite.config.js.map @@ -0,0 +1 @@ +{"version":3,"file":"vite.config.js","sourceRoot":"","sources":["vite.config.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,MAAM,sBAAsB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,eAAe,YAAY,CAAC;IAC1B,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,CAAC;CAC/C,CAAC,CAAC"} \ No newline at end of file diff --git a/examples/youcom-web-search/vite.config.ts b/examples/youcom-web-search/vite.config.ts new file mode 100644 index 000000000..147e46d63 --- /dev/null +++ b/examples/youcom-web-search/vite.config.ts @@ -0,0 +1,8 @@ +import tailwindcss from "@tailwindcss/vite"; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; +import { skybridge } from "skybridge/vite"; + +export default defineConfig({ + plugins: [react(), tailwindcss(), skybridge()], +}); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..1db646847 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,223 @@ +{ + "name": "@skybridge/monorepo", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@skybridge/monorepo", + "version": "0.0.0", + "license": "ISC", + "devDependencies": { + "@biomejs/biome": "^2.5.4", + "@total-typescript/tsconfig": "^1.0.4", + "@types/node": "^24.13.3", + "typescript": "^6.0.3" + }, + "engines": { + "node": ">=24.18.0" + } + }, + "node_modules/@biomejs/biome": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.7.tgz", + "integrity": "sha512-zr8K/DcY5tYsQOQwqMJ0AWElo6QgmgNI7idXgXLhevVszlt8RGVpesEJPqx3ThazLaOwjJ5Y8fz3BtH5fGZNsw==", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.5.7", + "@biomejs/cli-darwin-x64": "2.5.7", + "@biomejs/cli-linux-arm64": "2.5.7", + "@biomejs/cli-linux-arm64-musl": "2.5.7", + "@biomejs/cli-linux-x64": "2.5.7", + "@biomejs/cli-linux-x64-musl": "2.5.7", + "@biomejs/cli-win32-arm64": "2.5.7", + "@biomejs/cli-win32-x64": "2.5.7" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.7.tgz", + "integrity": "sha512-vxo/Ls3/PYdQWyLhYYcgMOCzQypAjcY+iihS8M0wW03l16TCLW4zqZzGo75gm1VdCMj38hTVZ31KBWrZ4G9dJw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.7.tgz", + "integrity": "sha512-Cd3Ga61amT/Yl/0x8elP5hhGYaFy4bw6WuysTgf7oo8TA5tJ5A1k+DkVoJ2BHbTVil51gTX9VPzArnrlLJ3Kyg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.7.tgz", + "integrity": "sha512-rR2QE0yF2GYSuYuKIa7pKvODGJqnOH+2eDREAM8wV+mWKSkMQKdAp4zXEZfTaxY8PMoNONnpgSWcBCyLDPDOKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.7.tgz", + "integrity": "sha512-xPI5yB6XlpDbNkS+bm1t42olw5c4l3UrlOmLg7KtLJvjvkNF/1V4tnUgfkylGIeb3u/T+BzMGYqgQhzjAoJzuQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.7.tgz", + "integrity": "sha512-FQgqJhscrqJUFptGaRSUJWlXAExwWcDwLuK49dvKfkQ1bB5SEEyFssnsxQY83Xm6jR0EbbX3+8+D5bfvYqUG2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.7.tgz", + "integrity": "sha512-rE5VZi+qtmPgQH+l7jVxYoZ18b/TiHEhulhMpjmCZH1PltSbjRcxNWywC3HZ9tYottG7ORkeTtoscBilKSBm0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.7.tgz", + "integrity": "sha512-Oq4x0CCwP4jirrcTywXs5kOGZ4v5vuEP+gWrbtjApOA2CL9F3F9GlIdQIci8AKSCa/zURanMRpX/4wQ7Am6hHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.7.tgz", + "integrity": "sha512-V+0wu/nrj2S+MhP4EQ0uHNolP0IALEsz45pg0WoKkHfDeh0+ItHwP/p7bX5RPoMOl9NkpHYWdYPhIcy2mACHvQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@total-typescript/tsconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@total-typescript/tsconfig/-/tsconfig-1.0.4.tgz", + "integrity": "sha512-fO4ctMPGz1kOFOQ4RCPBRBfMy3gDn+pegUfrGyUFRMv/Rd0ZM3/SHH3hFCYG4u6bPLG8OlmOGcBLDexvyr3A5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b35219261..4f18a8ab1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1371,6 +1371,85 @@ importers: specifier: ^5.9.3 version: 5.9.3 + examples/youcom-web-search: + dependencies: + '@alpic-ai/insights': + specifier: ^1.142.1 + version: 1.158.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(react@19.2.7)(skybridge@packages+core) + '@modelcontextprotocol/sdk': + specifier: ^1.29.0 + version: 1.29.0(zod@4.4.3) + '@t3-oss/env-core': + specifier: ^0.13.11 + version: 0.13.11(arktype@2.1.27)(typescript@5.9.3)(zod@4.4.3) + clsx: + specifier: ^2.1.1 + version: 2.1.1 + dotenv: + specifier: ^17.4.2 + version: 17.4.2 + express: + specifier: ^5.2.1 + version: 5.2.1 + lucide-react: + specifier: ^0.562.0 + version: 0.562.0(react@19.2.7) + react: + specifier: ^19.2.7 + version: 19.2.7 + react-dom: + specifier: ^19.2.7 + version: 19.2.7(react@19.2.7) + react-router-dom: + specifier: ^7.18.0 + version: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + skybridge: + specifier: workspace:* + version: link:../../packages/core + tailwind-merge: + specifier: ^3.6.0 + version: 3.6.0 + tailwindcss: + specifier: ^4.3.1 + version: 4.3.3 + zod: + specifier: ^4.4.3 + version: 4.4.3 + devDependencies: + '@skybridge/devtools': + specifier: ^1.2.3 + version: 1.2.7(arktype@2.1.27)(typescript@5.9.3) + '@tailwindcss/vite': + specifier: ^4.3.1 + version: 4.3.3(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.1)(tsx@4.23.1)(yaml@2.9.0)) + '@types/express': + specifier: ^5.0.6 + version: 5.0.6 + '@types/node': + specifier: ^22.20.0 + version: 22.20.1 + '@types/react': + specifier: ^19.2.17 + version: 19.2.17 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.17) + '@vitejs/plugin-react': + specifier: ^6.0.2 + version: 6.0.3(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.1)(tsx@4.23.1)(yaml@2.9.0)) + tsx: + specifier: ^4.22.4 + version: 4.23.1 + tw-animate-css: + specifier: ^1.4.0 + version: 1.4.0 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vite: + specifier: ^8.1.5 + version: 8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.1)(tsx@4.23.1)(yaml@2.9.0) + infrastructure: dependencies: aws-cdk-lib: