@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.
- package/README.md +134 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +26 -0
- package/dist/core/prompts.d.ts +3 -0
- package/dist/core/prompts.js +26 -0
- package/dist/core/resources.d.ts +3 -0
- package/dist/core/resources.js +45 -0
- package/dist/core/tools.d.ts +3 -0
- package/dist/core/tools.js +69 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +25 -0
- package/dist/loaders/agents.d.ts +2 -0
- package/dist/loaders/agents.js +20 -0
- package/dist/loaders/cache.d.ts +3 -0
- package/dist/loaders/cache.js +39 -0
- package/dist/loaders/embedded.d.ts +7 -0
- package/dist/loaders/embedded.js +50 -0
- package/dist/loaders/frontmatter.d.ts +17 -0
- package/dist/loaders/frontmatter.js +71 -0
- package/dist/loaders/skills.d.ts +2 -0
- package/dist/loaders/skills.js +55 -0
- package/dist/loaders/workflows.d.ts +2 -0
- package/dist/loaders/workflows.js +20 -0
- package/dist/paths.d.ts +8 -0
- package/dist/paths.js +16 -0
- package/dist/registry.d.ts +9 -0
- package/dist/registry.js +344 -0
- package/dist/types.d.ts +74 -0
- package/dist/types.js +2 -0
- package/dist/utils/routing.d.ts +2 -0
- package/dist/utils/routing.js +32 -0
- package/dist/utils/search.d.ts +4 -0
- package/dist/utils/search.js +42 -0
- package/dist/worker.d.ts +4 -0
- package/dist/worker.js +117 -0
- package/package.json +53 -0
package/dist/types.d.ts
ADDED
|
@@ -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,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,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
|
package/dist/worker.d.ts
ADDED
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
|
+
}
|