@getjack/jack 0.1.0 → 0.1.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 +16 -0
- package/package.json +47 -39
- package/src/commands/agents.ts +40 -9
- package/src/commands/cloud.ts +8 -4
- package/src/commands/down.ts +120 -69
- package/src/commands/init.ts +41 -3
- package/src/commands/mcp.ts +18 -0
- package/src/commands/new.ts +64 -334
- package/src/commands/projects.ts +139 -143
- package/src/commands/services.ts +315 -0
- package/src/commands/ship.ts +33 -139
- package/src/index.ts +27 -3
- package/src/lib/agent-files.ts +0 -41
- package/src/lib/agents.ts +238 -64
- package/src/lib/cloudflare-api.ts +3 -2
- package/src/lib/config.ts +8 -0
- package/src/lib/errors.ts +53 -0
- package/src/lib/hooks.ts +93 -41
- package/src/lib/mcp-config.ts +175 -0
- package/src/lib/project-operations.ts +793 -0
- package/src/lib/prompts.ts +15 -7
- package/src/lib/registry.ts +29 -1
- package/src/lib/services/db.ts +81 -0
- package/src/lib/services/index.ts +27 -0
- package/src/lib/telemetry.ts +10 -1
- package/src/mcp/README.md +142 -0
- package/src/mcp/resources/index.ts +87 -0
- package/src/mcp/server.ts +32 -0
- package/src/mcp/tools/index.ts +261 -0
- package/src/mcp/types.ts +29 -0
- package/src/mcp/utils.ts +147 -0
- package/src/templates/index.ts +2 -0
- package/src/templates/types.ts +16 -8
- package/templates/CLAUDE.md +105 -4
- package/templates/api/.jack.json +20 -1
- package/templates/api/src/index.ts +1 -1
- package/templates/miniapp/.jack.json +7 -5
package/src/lib/prompts.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { input, select } from "@inquirer/prompts";
|
|
2
2
|
import type { DetectedSecret } from "./env-parser.ts";
|
|
3
3
|
import { info, success, warn } from "./output.ts";
|
|
4
4
|
import { getSavedSecrets, getSecretsPath, maskSecret, saveSecrets } from "./secrets.ts";
|
|
@@ -107,12 +107,16 @@ export async function promptUseSecrets(): Promise<Record<string, string> | null>
|
|
|
107
107
|
}
|
|
108
108
|
console.error("");
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
console.error(" Esc to skip\n");
|
|
111
|
+
const action = await select({
|
|
111
112
|
message: "Use them for this project?",
|
|
112
|
-
|
|
113
|
+
choices: [
|
|
114
|
+
{ name: "1. Yes", value: "yes" },
|
|
115
|
+
{ name: "2. No", value: "no" },
|
|
116
|
+
],
|
|
113
117
|
});
|
|
114
118
|
|
|
115
|
-
if (
|
|
119
|
+
if (action === "yes") {
|
|
116
120
|
return saved;
|
|
117
121
|
}
|
|
118
122
|
|
|
@@ -147,10 +151,14 @@ export async function promptUseSecretsFromList(
|
|
|
147
151
|
return false;
|
|
148
152
|
}
|
|
149
153
|
|
|
150
|
-
|
|
154
|
+
console.error(" Esc to skip\n");
|
|
155
|
+
const action = await select({
|
|
151
156
|
message: "Use saved secrets for this project?",
|
|
152
|
-
|
|
157
|
+
choices: [
|
|
158
|
+
{ name: "1. Yes", value: "yes" },
|
|
159
|
+
{ name: "2. No", value: "no" },
|
|
160
|
+
],
|
|
153
161
|
});
|
|
154
162
|
|
|
155
|
-
return
|
|
163
|
+
return action === "yes";
|
|
156
164
|
}
|
package/src/lib/registry.ts
CHANGED
|
@@ -15,7 +15,9 @@ export interface Project {
|
|
|
15
15
|
workerId: string;
|
|
16
16
|
};
|
|
17
17
|
resources: {
|
|
18
|
-
|
|
18
|
+
services: {
|
|
19
|
+
db: string | null;
|
|
20
|
+
};
|
|
19
21
|
};
|
|
20
22
|
}
|
|
21
23
|
|
|
@@ -112,3 +114,29 @@ export async function getAllProjects(): Promise<Record<string, Project>> {
|
|
|
112
114
|
const registry = await readRegistry();
|
|
113
115
|
return registry.projects;
|
|
114
116
|
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get database name for a project
|
|
120
|
+
* @returns Database name or null if no database is configured
|
|
121
|
+
*/
|
|
122
|
+
export function getProjectDatabaseName(project: Project): string | null {
|
|
123
|
+
return project.resources?.services?.db ?? null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Update the database for a project using the new services structure
|
|
128
|
+
*/
|
|
129
|
+
export async function updateProjectDatabase(name: string, dbName: string | null): Promise<void> {
|
|
130
|
+
const project = await getProject(name);
|
|
131
|
+
if (!project) return;
|
|
132
|
+
|
|
133
|
+
await updateProject(name, {
|
|
134
|
+
resources: {
|
|
135
|
+
...project.resources,
|
|
136
|
+
services: {
|
|
137
|
+
...project.resources.services,
|
|
138
|
+
db: dbName,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { $ } from "bun";
|
|
2
|
+
|
|
3
|
+
export interface DatabaseInfo {
|
|
4
|
+
name: string;
|
|
5
|
+
id: string;
|
|
6
|
+
sizeBytes: number;
|
|
7
|
+
numTables: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get database info via wrangler d1 info
|
|
12
|
+
*/
|
|
13
|
+
export async function getDatabaseInfo(dbName: string): Promise<DatabaseInfo | null> {
|
|
14
|
+
const result = await $`wrangler d1 info ${dbName} --json`.nothrow().quiet();
|
|
15
|
+
|
|
16
|
+
if (result.exitCode !== 0) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const output = result.stdout.toString().trim();
|
|
22
|
+
const data = JSON.parse(output);
|
|
23
|
+
|
|
24
|
+
// wrangler d1 info --json returns:
|
|
25
|
+
// {
|
|
26
|
+
// "uuid": "...",
|
|
27
|
+
// "name": "...",
|
|
28
|
+
// "version": "...",
|
|
29
|
+
// "num_tables": N,
|
|
30
|
+
// "file_size": N
|
|
31
|
+
// }
|
|
32
|
+
return {
|
|
33
|
+
name: data.name || dbName,
|
|
34
|
+
id: data.uuid || "",
|
|
35
|
+
sizeBytes: data.file_size || 0,
|
|
36
|
+
numTables: data.num_tables || 0,
|
|
37
|
+
};
|
|
38
|
+
} catch (error) {
|
|
39
|
+
// Failed to parse JSON output
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Export database to SQL file
|
|
46
|
+
*/
|
|
47
|
+
export async function exportDatabase(dbName: string, outputPath: string): Promise<void> {
|
|
48
|
+
const result = await $`wrangler d1 export ${dbName} --output ${outputPath}`.nothrow().quiet();
|
|
49
|
+
|
|
50
|
+
if (result.exitCode !== 0) {
|
|
51
|
+
const stderr = result.stderr.toString().trim();
|
|
52
|
+
throw new Error(stderr || `Failed to export database ${dbName} to ${outputPath}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Generate export filename with timestamp
|
|
58
|
+
* Format: {db-name}-{YYYY-MM-DD-HHMMSS}.sql
|
|
59
|
+
*/
|
|
60
|
+
export function generateExportFilename(dbName: string): string {
|
|
61
|
+
const now = new Date();
|
|
62
|
+
const year = now.getFullYear();
|
|
63
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
64
|
+
const day = String(now.getDate()).padStart(2, "0");
|
|
65
|
+
const hours = String(now.getHours()).padStart(2, "0");
|
|
66
|
+
const minutes = String(now.getMinutes()).padStart(2, "0");
|
|
67
|
+
const seconds = String(now.getSeconds()).padStart(2, "0");
|
|
68
|
+
return `${dbName}-${year}-${month}-${day}-${hours}${minutes}${seconds}.sql`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Delete database via wrangler
|
|
73
|
+
*/
|
|
74
|
+
export async function deleteDatabase(dbName: string): Promise<void> {
|
|
75
|
+
const result = await $`wrangler d1 delete ${dbName} --skip-confirmation`.nothrow().quiet();
|
|
76
|
+
|
|
77
|
+
if (result.exitCode !== 0) {
|
|
78
|
+
const stderr = result.stderr.toString().trim();
|
|
79
|
+
throw new Error(stderr || `Failed to delete database ${dbName}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Normalized service type constants
|
|
2
|
+
export const ServiceType = {
|
|
3
|
+
DB: "db",
|
|
4
|
+
KV: "kv",
|
|
5
|
+
CRON: "cron",
|
|
6
|
+
QUEUE: "queue",
|
|
7
|
+
STORAGE: "storage",
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
export type ServiceTypeKey = keyof typeof ServiceType;
|
|
11
|
+
export type ServiceTypeValue = (typeof ServiceType)[ServiceTypeKey];
|
|
12
|
+
|
|
13
|
+
// Template metadata interface for service requirements
|
|
14
|
+
export interface TemplateServiceRequirements {
|
|
15
|
+
requires: ServiceTypeKey[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Helper to check if template requires a service
|
|
19
|
+
export function templateRequiresService(
|
|
20
|
+
templateMetadata: TemplateServiceRequirements | undefined,
|
|
21
|
+
serviceType: ServiceTypeKey,
|
|
22
|
+
): boolean {
|
|
23
|
+
if (!templateMetadata) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
return templateMetadata.requires.includes(serviceType);
|
|
27
|
+
}
|
package/src/lib/telemetry.ts
CHANGED
|
@@ -159,6 +159,10 @@ export async function track(event: EventName, properties?: Record<string, unknow
|
|
|
159
159
|
// THE MAGIC: withTelemetry() wrapper
|
|
160
160
|
// Commands wrapped with this get automatic tracking
|
|
161
161
|
// ============================================
|
|
162
|
+
export interface TelemetryOptions {
|
|
163
|
+
platform?: "cli" | "mcp";
|
|
164
|
+
}
|
|
165
|
+
|
|
162
166
|
/**
|
|
163
167
|
* Wrap a command function with automatic telemetry tracking
|
|
164
168
|
* Tracks command_invoked, command_completed, and command_failed events
|
|
@@ -167,22 +171,27 @@ export async function track(event: EventName, properties?: Record<string, unknow
|
|
|
167
171
|
export function withTelemetry<T extends (...args: any[]) => Promise<any>>(
|
|
168
172
|
commandName: string,
|
|
169
173
|
fn: T,
|
|
174
|
+
options?: TelemetryOptions,
|
|
170
175
|
): T {
|
|
176
|
+
const platform = options?.platform ?? "cli";
|
|
177
|
+
|
|
171
178
|
return (async (...args: Parameters<T>) => {
|
|
172
179
|
// Fire-and-forget: don't await track() to avoid blocking command execution
|
|
173
|
-
track(Events.COMMAND_INVOKED, { command: commandName });
|
|
180
|
+
track(Events.COMMAND_INVOKED, { command: commandName, platform });
|
|
174
181
|
const start = Date.now();
|
|
175
182
|
|
|
176
183
|
try {
|
|
177
184
|
const result = await fn(...args);
|
|
178
185
|
track(Events.COMMAND_COMPLETED, {
|
|
179
186
|
command: commandName,
|
|
187
|
+
platform,
|
|
180
188
|
duration_ms: Date.now() - start,
|
|
181
189
|
});
|
|
182
190
|
return result;
|
|
183
191
|
} catch (error) {
|
|
184
192
|
track(Events.COMMAND_FAILED, {
|
|
185
193
|
command: commandName,
|
|
194
|
+
platform,
|
|
186
195
|
error_type: classifyError(error),
|
|
187
196
|
duration_ms: Date.now() - start,
|
|
188
197
|
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Jack MCP Server
|
|
2
|
+
|
|
3
|
+
This directory contains the Model Context Protocol (MCP) server implementation for jack CLI.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
src/mcp/
|
|
9
|
+
├── server.ts # Main MCP server setup and initialization
|
|
10
|
+
├── types.ts # TypeScript types for MCP responses
|
|
11
|
+
├── utils.ts # Response formatting utilities
|
|
12
|
+
├── tools/
|
|
13
|
+
│ └── index.ts # Tool registration and handlers
|
|
14
|
+
└── resources/
|
|
15
|
+
└── index.ts # Resource registration (agents_md)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Key Components
|
|
19
|
+
|
|
20
|
+
### `server.ts`
|
|
21
|
+
- Creates and configures the MCP server instance
|
|
22
|
+
- Registers tools and resources
|
|
23
|
+
- Exports `startMcpServer()` for stdio transport
|
|
24
|
+
|
|
25
|
+
### `tools/index.ts`
|
|
26
|
+
- Centralized tool registration and dispatch
|
|
27
|
+
- Implements 4 core tools:
|
|
28
|
+
- `create_project` - Create new Cloudflare Workers project
|
|
29
|
+
- `deploy_project` - Deploy existing project
|
|
30
|
+
- `get_project_status` - Get project status info
|
|
31
|
+
- `list_projects` - List all projects with filters
|
|
32
|
+
- Each tool wraps corresponding function from `lib/project-operations.ts`
|
|
33
|
+
- All tools track telemetry with `platform: 'mcp'`
|
|
34
|
+
|
|
35
|
+
### `resources/index.ts`
|
|
36
|
+
- Registers `agents://context` resource
|
|
37
|
+
- Reads and combines AGENTS.md and CLAUDE.md from project directory
|
|
38
|
+
- Provides AI agents with project-specific context
|
|
39
|
+
|
|
40
|
+
### `types.ts`
|
|
41
|
+
- Defines `McpToolResponse<T>` interface
|
|
42
|
+
- Enum of error codes for structured error handling
|
|
43
|
+
- `McpServerOptions` configuration interface
|
|
44
|
+
|
|
45
|
+
### `utils.ts`
|
|
46
|
+
- `formatSuccessResponse()` - Creates success response with metadata
|
|
47
|
+
- `formatErrorResponse()` - Classifies errors and provides suggestions
|
|
48
|
+
- `classifyMcpError()` - Maps error messages to error codes
|
|
49
|
+
- `getSuggestionForError()` - Returns actionable suggestions
|
|
50
|
+
|
|
51
|
+
## Response Format
|
|
52
|
+
|
|
53
|
+
All tools return structured JSON responses:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
{
|
|
57
|
+
success: boolean
|
|
58
|
+
data?: T
|
|
59
|
+
error?: {
|
|
60
|
+
code: string // Machine-readable
|
|
61
|
+
message: string // Human-readable
|
|
62
|
+
suggestion?: string // Actionable next steps
|
|
63
|
+
}
|
|
64
|
+
meta?: {
|
|
65
|
+
duration_ms: number
|
|
66
|
+
jack_version: string
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Integration with Project Operations
|
|
72
|
+
|
|
73
|
+
The MCP tools are thin wrappers around functions in `lib/project-operations.ts`:
|
|
74
|
+
|
|
75
|
+
- `createProject()` - Handles project creation with templates
|
|
76
|
+
- `deployProject()` - Manages builds and deployments
|
|
77
|
+
- `getProjectStatus()` - Fetches project status
|
|
78
|
+
- `listAllProjects()` - Lists all registered projects
|
|
79
|
+
|
|
80
|
+
All operations run in "silent mode" (no console output) when called from MCP.
|
|
81
|
+
|
|
82
|
+
## Telemetry
|
|
83
|
+
|
|
84
|
+
All tool calls are tracked via the existing telemetry system:
|
|
85
|
+
|
|
86
|
+
- Automatic tracking: `command_invoked`, `command_completed`, `command_failed`
|
|
87
|
+
- Business events: `project_created`, `deploy_started`
|
|
88
|
+
- All events tagged with `platform: 'mcp'`
|
|
89
|
+
|
|
90
|
+
## Development
|
|
91
|
+
|
|
92
|
+
### Running the Server
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Start server via CLI (uses stdio transport)
|
|
96
|
+
jack mcp serve
|
|
97
|
+
|
|
98
|
+
# With explicit project path
|
|
99
|
+
jack mcp serve --project /path/to/project
|
|
100
|
+
|
|
101
|
+
# The server communicates via stdin/stdout
|
|
102
|
+
# Do NOT use console.log in MCP code
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Adding New Tools
|
|
106
|
+
|
|
107
|
+
1. Add tool definition to `tools/list` handler in `tools/index.ts`
|
|
108
|
+
2. Add case to `tools/call` handler switch statement
|
|
109
|
+
3. Create zod schema for tool parameters
|
|
110
|
+
4. Wrap function from `project-operations.ts` with telemetry
|
|
111
|
+
5. Return formatted response using utils
|
|
112
|
+
|
|
113
|
+
### Testing
|
|
114
|
+
|
|
115
|
+
The MCP server can be tested using:
|
|
116
|
+
- Claude Desktop (see `/docs/mcp-configuration.md`)
|
|
117
|
+
- MCP Inspector (https://github.com/modelcontextprotocol/inspector)
|
|
118
|
+
- Manual JSON-RPC over stdio
|
|
119
|
+
|
|
120
|
+
## Error Handling
|
|
121
|
+
|
|
122
|
+
Errors are classified into categories:
|
|
123
|
+
|
|
124
|
+
- `AUTH_FAILED` - Authentication issues
|
|
125
|
+
- `WRANGLER_AUTH_EXPIRED` - Wrangler needs re-auth
|
|
126
|
+
- `PROJECT_NOT_FOUND` - Project doesn't exist
|
|
127
|
+
- `TEMPLATE_NOT_FOUND` - Invalid template
|
|
128
|
+
- `BUILD_FAILED` - Build errors
|
|
129
|
+
- `DEPLOY_FAILED` - Deployment errors
|
|
130
|
+
- `VALIDATION_ERROR` - Invalid parameters
|
|
131
|
+
- `INTERNAL_ERROR` - Unexpected failures
|
|
132
|
+
|
|
133
|
+
Each error code includes a helpful suggestion for resolution.
|
|
134
|
+
|
|
135
|
+
## Protocol Compliance
|
|
136
|
+
|
|
137
|
+
This implementation follows the MCP specification:
|
|
138
|
+
- Uses stdio transport for client communication
|
|
139
|
+
- Implements required handlers: `tools/list`, `tools/call`
|
|
140
|
+
- Implements optional handlers: `resources/list`, `resources/read`
|
|
141
|
+
- Returns properly formatted JSON-RPC responses
|
|
142
|
+
- Never writes to stdout except for protocol messages
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import type { Server as McpServer } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import {
|
|
5
|
+
ListResourcesRequestSchema,
|
|
6
|
+
ReadResourceRequestSchema,
|
|
7
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
8
|
+
import type { McpServerOptions } from "../types.ts";
|
|
9
|
+
|
|
10
|
+
export function registerResources(server: McpServer, options: McpServerOptions) {
|
|
11
|
+
// Register resource list handler
|
|
12
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
13
|
+
return {
|
|
14
|
+
resources: [
|
|
15
|
+
{
|
|
16
|
+
uri: "agents://context",
|
|
17
|
+
name: "Agent Context Files",
|
|
18
|
+
description:
|
|
19
|
+
"Project-specific context files (AGENTS.md, CLAUDE.md) for AI agents working on this project",
|
|
20
|
+
mimeType: "text/markdown",
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Register resource read handler
|
|
27
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
28
|
+
const uri = request.params.uri;
|
|
29
|
+
|
|
30
|
+
if (uri === "agents://context") {
|
|
31
|
+
const projectPath = options.projectPath ?? process.cwd();
|
|
32
|
+
const agentsPath = join(projectPath, "AGENTS.md");
|
|
33
|
+
const claudePath = join(projectPath, "CLAUDE.md");
|
|
34
|
+
|
|
35
|
+
const contents: string[] = [];
|
|
36
|
+
|
|
37
|
+
// Try to read AGENTS.md
|
|
38
|
+
if (existsSync(agentsPath)) {
|
|
39
|
+
try {
|
|
40
|
+
const agentsContent = await Bun.file(agentsPath).text();
|
|
41
|
+
contents.push("# AGENTS.md\n\n");
|
|
42
|
+
contents.push(agentsContent);
|
|
43
|
+
} catch {
|
|
44
|
+
// Ignore read errors
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Try to read CLAUDE.md
|
|
49
|
+
if (existsSync(claudePath)) {
|
|
50
|
+
try {
|
|
51
|
+
const claudeContent = await Bun.file(claudePath).text();
|
|
52
|
+
if (contents.length > 0) {
|
|
53
|
+
contents.push("\n\n---\n\n");
|
|
54
|
+
}
|
|
55
|
+
contents.push("# CLAUDE.md\n\n");
|
|
56
|
+
contents.push(claudeContent);
|
|
57
|
+
} catch {
|
|
58
|
+
// Ignore read errors
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (contents.length === 0) {
|
|
63
|
+
return {
|
|
64
|
+
contents: [
|
|
65
|
+
{
|
|
66
|
+
uri,
|
|
67
|
+
mimeType: "text/plain",
|
|
68
|
+
text: "No agent context files found in project directory",
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
contents: [
|
|
76
|
+
{
|
|
77
|
+
uri,
|
|
78
|
+
mimeType: "text/markdown",
|
|
79
|
+
text: contents.join(""),
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
throw new Error(`Unknown resource URI: ${uri}`);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Server as McpServer } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import pkg from "../../package.json" with { type: "json" };
|
|
4
|
+
import { registerResources } from "./resources/index.ts";
|
|
5
|
+
import { registerTools } from "./tools/index.ts";
|
|
6
|
+
import type { McpServerOptions } from "./types.ts";
|
|
7
|
+
|
|
8
|
+
export async function createMcpServer(options: McpServerOptions = {}) {
|
|
9
|
+
const server = new McpServer(
|
|
10
|
+
{
|
|
11
|
+
name: "jack",
|
|
12
|
+
version: pkg.version,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
capabilities: {
|
|
16
|
+
tools: {},
|
|
17
|
+
resources: {},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
registerTools(server, options);
|
|
23
|
+
registerResources(server, options);
|
|
24
|
+
|
|
25
|
+
return server;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function startMcpServer(options: McpServerOptions = {}) {
|
|
29
|
+
const server = await createMcpServer(options);
|
|
30
|
+
const transport = new StdioServerTransport();
|
|
31
|
+
await server.connect(transport);
|
|
32
|
+
}
|