@joelbonito/mcp-server 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,74 @@
1
+ export interface AgentMeta {
2
+ name: string;
3
+ description: string;
4
+ tools: string[];
5
+ model: string;
6
+ skills: string[];
7
+ }
8
+ export interface Agent {
9
+ meta: AgentMeta;
10
+ body: string;
11
+ raw: string;
12
+ }
13
+ export interface SkillMeta {
14
+ name: string;
15
+ description: string;
16
+ allowedTools?: string[];
17
+ version?: string;
18
+ priority?: string;
19
+ }
20
+ export interface SubFile {
21
+ filename: string;
22
+ content: string;
23
+ }
24
+ export interface Skill {
25
+ meta: SkillMeta;
26
+ body: string;
27
+ subFiles: SubFile[];
28
+ hasScripts: boolean;
29
+ raw: string;
30
+ }
31
+ export interface WorkflowMeta {
32
+ description: string;
33
+ }
34
+ export interface Workflow {
35
+ meta: WorkflowMeta;
36
+ body: string;
37
+ raw: string;
38
+ }
39
+ export interface AgentSummary {
40
+ name: string;
41
+ description: string;
42
+ skills: string[];
43
+ }
44
+ export interface SkillSummary {
45
+ name: string;
46
+ description: string;
47
+ }
48
+ export interface WorkflowSummary {
49
+ name: string;
50
+ description: string;
51
+ }
52
+ export interface RouteResult {
53
+ agents: AgentSummary[];
54
+ skills: SkillSummary[];
55
+ reasoning: string;
56
+ }
57
+ export interface SearchResult {
58
+ type: "agent" | "skill" | "workflow";
59
+ name: string;
60
+ matches: string[];
61
+ }
62
+ export interface ContentCache {
63
+ agents: Map<string, Agent>;
64
+ skills: Map<string, Skill>;
65
+ workflows: Map<string, Workflow>;
66
+ architecture: string;
67
+ instructions: string;
68
+ }
69
+ export interface ContentLoader {
70
+ loadAgents(): Promise<Map<string, Agent>>;
71
+ loadSkills(): Promise<Map<string, Skill>>;
72
+ loadWorkflows(): Promise<Map<string, Workflow>>;
73
+ loadFile(path: string): Promise<string>;
74
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,2 @@
1
+ import type { ContentCache, RouteResult } from "../types.js";
2
+ export declare function routeTask(request: string, cache: ContentCache): RouteResult;
@@ -0,0 +1,32 @@
1
+ import { ROUTING_KEYWORDS } from "../constants.js";
2
+ export function routeTask(request, cache) {
3
+ const normalized = request.toLowerCase();
4
+ const scores = [];
5
+ for (const [agent, keywords] of Object.entries(ROUTING_KEYWORDS)) {
6
+ const score = keywords.filter((kw) => normalized.includes(kw)).length;
7
+ if (score > 0)
8
+ scores.push({ name: agent, score });
9
+ }
10
+ scores.sort((a, b) => b.score - a.score);
11
+ const top = scores.slice(0, 3);
12
+ const agents = top.map(({ name }) => {
13
+ const agent = cache.agents.get(name);
14
+ return {
15
+ name,
16
+ description: agent?.meta.description ?? "",
17
+ skills: agent?.meta.skills ?? [],
18
+ };
19
+ });
20
+ const skillNames = new Set(agents.flatMap((a) => a.skills));
21
+ const skills = [...skillNames]
22
+ .map((name) => {
23
+ const skill = cache.skills.get(name);
24
+ return skill ? { name, description: skill.meta.description } : null;
25
+ })
26
+ .filter((s) => s !== null);
27
+ const reasoning = top.length > 0
28
+ ? `Matched ${top.map((t) => `${t.name} (score: ${t.score})`).join(", ")} based on keyword analysis of: "${request}"`
29
+ : `No strong keyword match found for: "${request}". Consider using search_content for broader results.`;
30
+ return { agents, skills, reasoning };
31
+ }
32
+ //# sourceMappingURL=routing.js.map
@@ -0,0 +1,4 @@
1
+ import type { ContentCache, SearchResult } from "../types.js";
2
+ type Scope = "all" | "agents" | "skills" | "workflows";
3
+ export declare function searchContent(query: string, scope: Scope, maxResults: number, cache: ContentCache): SearchResult[];
4
+ export {};
@@ -0,0 +1,42 @@
1
+ export function searchContent(query, scope, maxResults, cache) {
2
+ const results = [];
3
+ const lowerQuery = query.toLowerCase();
4
+ if (scope === "all" || scope === "agents") {
5
+ for (const [name, agent] of cache.agents) {
6
+ const matches = findMatches(agent.raw, lowerQuery);
7
+ if (matches.length > 0) {
8
+ results.push({ type: "agent", name, matches });
9
+ }
10
+ }
11
+ }
12
+ if (scope === "all" || scope === "skills") {
13
+ for (const [name, skill] of cache.skills) {
14
+ const matches = findMatches(skill.raw, lowerQuery);
15
+ if (matches.length > 0) {
16
+ results.push({ type: "skill", name, matches });
17
+ }
18
+ }
19
+ }
20
+ if (scope === "all" || scope === "workflows") {
21
+ for (const [name, workflow] of cache.workflows) {
22
+ const matches = findMatches(workflow.raw, lowerQuery);
23
+ if (matches.length > 0) {
24
+ results.push({ type: "workflow", name, matches });
25
+ }
26
+ }
27
+ }
28
+ return results.slice(0, maxResults);
29
+ }
30
+ function findMatches(content, lowerQuery, maxPerItem = 3) {
31
+ const lines = content.split("\n");
32
+ const matches = [];
33
+ for (const line of lines) {
34
+ if (line.toLowerCase().includes(lowerQuery)) {
35
+ matches.push(line.trim());
36
+ if (matches.length >= maxPerItem)
37
+ break;
38
+ }
39
+ }
40
+ return matches;
41
+ }
42
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1,4 @@
1
+ declare const _default: {
2
+ fetch(request: Request): Promise<Response>;
3
+ };
4
+ export default _default;
package/dist/worker.js ADDED
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Cloudflare Workers entrypoint for Inove AI MCP Server.
3
+ * Serves the same 21 agents, 41 skills, 22 workflows as the stdio server
4
+ * via Streamable HTTP transport (stateless, public).
5
+ *
6
+ * WARNING: This module must NOT import paths.ts or cache.ts — they use node:fs.
7
+ * Use EmbeddedLoader directly for Worker-compatible content loading.
8
+ */
9
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
+ import { WebStandardStreamableHTTPServerTransport, } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
11
+ import { SERVER_NAME, SERVER_VERSION } from "./constants.js";
12
+ import { EmbeddedLoader } from "./loaders/embedded.js";
13
+ import { registerResources } from "./core/resources.js";
14
+ import { registerTools } from "./core/tools.js";
15
+ import { registerPrompts } from "./core/prompts.js";
16
+ const MAX_BODY_SIZE = 64 * 1024; // 64 KB — generous for JSON-RPC
17
+ // Module-level cache — Promise-based singleton to prevent race conditions on cold start
18
+ let cachePromise = null;
19
+ function getWorkerCache() {
20
+ if (!cachePromise) {
21
+ cachePromise = (async () => {
22
+ const loader = new EmbeddedLoader();
23
+ const [agents, skills, workflows, architecture, instructions] = await Promise.all([
24
+ loader.loadAgents(),
25
+ loader.loadSkills(),
26
+ loader.loadWorkflows(),
27
+ loader.loadFile("ARCHITECTURE"),
28
+ loader.loadFile("INSTRUCTIONS"),
29
+ ]);
30
+ return { agents, skills, workflows, architecture, instructions };
31
+ })().catch((err) => {
32
+ cachePromise = null; // Allow retry on next request
33
+ throw err;
34
+ });
35
+ }
36
+ return cachePromise;
37
+ }
38
+ // CORS: Wildcard is intentional — this is a public, read-only, credential-free API.
39
+ // MCP clients (Claude Desktop, Cursor, VS Code) connect from any origin.
40
+ const CORS_HEADERS = {
41
+ "Access-Control-Allow-Origin": "*",
42
+ "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
43
+ "Access-Control-Allow-Headers": "Content-Type, mcp-session-id",
44
+ };
45
+ const SECURITY_HEADERS = {
46
+ "X-Content-Type-Options": "nosniff",
47
+ "X-Frame-Options": "DENY",
48
+ "Referrer-Policy": "no-referrer",
49
+ };
50
+ function jsonResponse(data, status = 200) {
51
+ return new Response(JSON.stringify(data), {
52
+ status,
53
+ headers: { "Content-Type": "application/json", ...CORS_HEADERS, ...SECURITY_HEADERS },
54
+ });
55
+ }
56
+ export default {
57
+ async fetch(request) {
58
+ try {
59
+ const url = new URL(request.url);
60
+ // CORS preflight
61
+ if (request.method === "OPTIONS") {
62
+ return new Response(null, { status: 204, headers: { ...CORS_HEADERS, ...SECURITY_HEADERS } });
63
+ }
64
+ // Health check
65
+ if (url.pathname === "/" || url.pathname === "/health") {
66
+ const c = await getWorkerCache();
67
+ return jsonResponse({
68
+ status: "ok",
69
+ name: SERVER_NAME,
70
+ version: SERVER_VERSION,
71
+ agents: c.agents.size,
72
+ skills: c.skills.size,
73
+ workflows: c.workflows.size,
74
+ });
75
+ }
76
+ // MCP endpoint
77
+ if (url.pathname === "/mcp") {
78
+ // Body size guard — prevent DoS via oversized payloads
79
+ const contentLength = parseInt(request.headers.get("content-length") ?? "0", 10);
80
+ if (contentLength > MAX_BODY_SIZE) {
81
+ return jsonResponse({ error: "Payload too large" }, 413);
82
+ }
83
+ const c = await getWorkerCache();
84
+ const server = new McpServer({
85
+ name: SERVER_NAME,
86
+ version: SERVER_VERSION,
87
+ });
88
+ registerResources(server, c);
89
+ registerTools(server, c);
90
+ registerPrompts(server, c);
91
+ const transport = new WebStandardStreamableHTTPServerTransport({
92
+ sessionIdGenerator: undefined, // Stateless — no session management
93
+ });
94
+ await server.connect(transport);
95
+ const response = await transport.handleRequest(request);
96
+ if (!response) {
97
+ return jsonResponse({ error: "Invalid MCP request" }, 400);
98
+ }
99
+ // Inject CORS + security headers into MCP response
100
+ const headers = new Headers(response.headers);
101
+ for (const [key, value] of Object.entries({ ...CORS_HEADERS, ...SECURITY_HEADERS })) {
102
+ headers.set(key, value);
103
+ }
104
+ return new Response(response.body, {
105
+ status: response.status,
106
+ statusText: response.statusText,
107
+ headers,
108
+ });
109
+ }
110
+ return jsonResponse({ error: "Not found. Use /mcp for MCP protocol or / for health check." }, 404);
111
+ }
112
+ catch (error) {
113
+ return jsonResponse({ error: "Internal server error", message: error instanceof Error ? error.message : "Unknown error" }, 500);
114
+ }
115
+ },
116
+ };
117
+ //# sourceMappingURL=worker.js.map
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@joelbonito/mcp-server",
3
+ "version": "5.0.1",
4
+ "description": "MCP server for Inove AI Framework — agents, skills, and workflows as MCP resources, tools and prompts",
5
+ "license": "MIT",
6
+ "author": "Inove AI <hello@inove.ai>",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/JoelBonito/inove-ai-framework",
10
+ "directory": "mcp-server"
11
+ },
12
+ "homepage": "https://github.com/JoelBonito/inove-ai-framework/tree/main/mcp-server#readme",
13
+ "keywords": ["mcp", "ai-framework", "agents", "skills", "workflows", "claude", "model-context-protocol"],
14
+ "type": "module",
15
+ "main": "dist/index.js",
16
+ "bin": {
17
+ "inove-mcp": "dist/index.js"
18
+ },
19
+ "scripts": {
20
+ "prebuild": "tsx scripts/bundle-content.ts",
21
+ "build": "tsc",
22
+ "postbuild": "chmod +x dist/index.js",
23
+ "dev": "tsx src/index.ts",
24
+ "start": "node dist/index.js",
25
+ "test": "vitest run",
26
+ "test:watch": "vitest",
27
+ "typecheck": "tsc --noEmit",
28
+ "dev:workers": "wrangler dev",
29
+ "deploy:workers": "wrangler deploy"
30
+ },
31
+ "dependencies": {
32
+ "@modelcontextprotocol/sdk": "^1.26.0",
33
+ "yaml": "^2.7.0",
34
+ "zod": "^3.24.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^22.0.0",
38
+ "tsx": "^4.19.0",
39
+ "typescript": "^5.7.0",
40
+ "vitest": "^3.0.0",
41
+ "wrangler": "^4.0.0"
42
+ },
43
+ "engines": {
44
+ "node": ">=22.0.0"
45
+ },
46
+ "files": [
47
+ "dist/",
48
+ "!dist/**/*.map"
49
+ ],
50
+ "publishConfig": {
51
+ "access": "public"
52
+ }
53
+ }