@kya-os/create-mcpi-app 1.7.17 → 1.7.20

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.
Files changed (98) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-test$colon$coverage.log +315 -0
  3. package/.turbo/turbo-test.log +95 -0
  4. package/CHANGELOG.md +372 -0
  5. package/IMPLEMENTATION_SUMMARY.md +108 -0
  6. package/REMEDIATION_PLAN.md +99 -0
  7. package/coverage/base.css +224 -0
  8. package/coverage/block-navigation.js +87 -0
  9. package/coverage/clover.xml +252 -0
  10. package/coverage/config-builder.ts.html +580 -0
  11. package/coverage/coverage-final.json +7 -0
  12. package/coverage/favicon.png +0 -0
  13. package/coverage/fetch-cloudflare-mcpi-template.ts.html +7006 -0
  14. package/coverage/generate-config.ts.html +436 -0
  15. package/coverage/generate-identity.ts.html +574 -0
  16. package/coverage/index.html +191 -0
  17. package/coverage/install.ts.html +322 -0
  18. package/coverage/prettify.css +1 -0
  19. package/coverage/prettify.js +2 -0
  20. package/coverage/sort-arrow-sprite.png +0 -0
  21. package/coverage/sorter.js +210 -0
  22. package/coverage/validate-project-structure.ts.html +466 -0
  23. package/dist/.tsbuildinfo +1 -1
  24. package/dist/helpers/__tests__/config-builder.spec.d.ts +8 -0
  25. package/dist/helpers/__tests__/config-builder.spec.d.ts.map +1 -0
  26. package/dist/helpers/__tests__/config-builder.spec.js +182 -0
  27. package/dist/helpers/__tests__/config-builder.spec.js.map +1 -0
  28. package/dist/helpers/config-builder.d.ts +58 -0
  29. package/dist/helpers/config-builder.d.ts.map +1 -0
  30. package/dist/helpers/config-builder.js +102 -0
  31. package/dist/helpers/config-builder.js.map +1 -0
  32. package/dist/helpers/create.d.ts +1 -0
  33. package/dist/helpers/create.d.ts.map +1 -1
  34. package/dist/helpers/create.js +2 -1
  35. package/dist/helpers/create.js.map +1 -1
  36. package/dist/helpers/fetch-cloudflare-mcpi-template.d.ts +1 -0
  37. package/dist/helpers/fetch-cloudflare-mcpi-template.d.ts.map +1 -1
  38. package/dist/helpers/fetch-cloudflare-mcpi-template.js +209 -174
  39. package/dist/helpers/fetch-cloudflare-mcpi-template.js.map +1 -1
  40. package/dist/helpers/fetch-mcpi-template.d.ts.map +1 -1
  41. package/dist/helpers/fetch-mcpi-template.js +18 -3
  42. package/dist/helpers/fetch-mcpi-template.js.map +1 -1
  43. package/dist/helpers/generate-config.d.ts.map +1 -1
  44. package/dist/helpers/generate-config.js +27 -40
  45. package/dist/helpers/generate-config.js.map +1 -1
  46. package/dist/helpers/install.js +5 -0
  47. package/dist/helpers/install.js.map +1 -1
  48. package/dist/index.js +2 -0
  49. package/dist/index.js.map +1 -1
  50. package/package.json +18 -9
  51. package/scripts/prepare-pack.js +47 -0
  52. package/scripts/validate-no-workspace.js +79 -0
  53. package/src/__tests__/cloudflare-template.test.ts +488 -0
  54. package/src/__tests__/helpers/fetch-cloudflare-mcpi-template.test.ts +337 -0
  55. package/src/__tests__/helpers/generate-config.test.ts +312 -0
  56. package/src/__tests__/helpers/generate-identity.test.ts +271 -0
  57. package/src/__tests__/helpers/install.test.ts +362 -0
  58. package/src/__tests__/helpers/validate-project-structure.test.ts +467 -0
  59. package/src/__tests__.bak/regression.test.ts +434 -0
  60. package/src/effects/index.ts +80 -0
  61. package/src/helpers/__tests__/config-builder.spec.ts +231 -0
  62. package/src/helpers/apply-identity-preset.ts +209 -0
  63. package/src/helpers/config-builder.ts +165 -0
  64. package/src/helpers/copy-template.ts +11 -0
  65. package/src/helpers/create.ts +239 -0
  66. package/src/helpers/fetch-cloudflare-mcpi-template.ts +2311 -0
  67. package/src/helpers/fetch-cloudflare-template.ts +361 -0
  68. package/src/helpers/fetch-mcpi-template.ts +236 -0
  69. package/src/helpers/fetch-xmcp-template.ts +153 -0
  70. package/src/helpers/generate-config.ts +117 -0
  71. package/src/helpers/generate-identity.ts +163 -0
  72. package/src/helpers/identity-manager.ts +186 -0
  73. package/src/helpers/install.ts +79 -0
  74. package/src/helpers/rename.ts +17 -0
  75. package/src/helpers/validate-project-structure.ts +127 -0
  76. package/src/index.ts +480 -0
  77. package/src/utils/check-node.ts +17 -0
  78. package/src/utils/is-folder-empty.ts +60 -0
  79. package/src/utils/validate-project-name.ts +132 -0
  80. package/test-cloudflare/README.md +164 -0
  81. package/test-cloudflare/package.json +28 -0
  82. package/test-cloudflare/src/index.ts +340 -0
  83. package/test-cloudflare/src/tools/greet.ts +19 -0
  84. package/test-cloudflare/tests/cache-invalidation.test.ts +410 -0
  85. package/test-cloudflare/tests/cors-security.test.ts +349 -0
  86. package/test-cloudflare/tests/delegation.test.ts +335 -0
  87. package/test-cloudflare/tests/do-routing.test.ts +314 -0
  88. package/test-cloudflare/tests/integration.test.ts +205 -0
  89. package/test-cloudflare/tests/session-management.test.ts +359 -0
  90. package/test-cloudflare/tsconfig.json +22 -0
  91. package/test-cloudflare/vitest.config.ts +9 -0
  92. package/test-cloudflare/wrangler.toml +37 -0
  93. package/test-node/README.md +44 -0
  94. package/test-node/package.json +23 -0
  95. package/test-node/src/tools/greet.ts +25 -0
  96. package/test-node/xmcp.config.ts +20 -0
  97. package/tsconfig.json +26 -0
  98. package/vitest.config.ts +14 -0
@@ -0,0 +1,359 @@
1
+ import { describe, test, expect, vi, beforeEach } from 'vitest';
2
+
3
+ /**
4
+ * Session Management Tests
5
+ * Tests proper session tracking, TTL, and MCP session ID handling
6
+ */
7
+ describe('Session Management', () => {
8
+
9
+ // Mock KV namespace for session storage
10
+ const mockSessionStorage = {
11
+ get: vi.fn(),
12
+ put: vi.fn(),
13
+ delete: vi.fn()
14
+ };
15
+
16
+ beforeEach(() => {
17
+ vi.clearAllMocks();
18
+ });
19
+
20
+ describe('Session ID Reading', () => {
21
+ test('should read MCP session ID from Claude Desktop headers', () => {
22
+ const headers = new Headers({
23
+ 'mcp-session-id': 'claude-desktop-session-123'
24
+ });
25
+
26
+ const sessionId = headers.get('mcp-session-id') ||
27
+ headers.get('Mcp-Session-Id');
28
+
29
+ expect(sessionId).toBe('claude-desktop-session-123');
30
+ });
31
+
32
+ test('should handle both header casings', () => {
33
+ const headers1 = new Headers({
34
+ 'mcp-session-id': 'session-lower'
35
+ });
36
+ const headers2 = new Headers({
37
+ 'Mcp-Session-Id': 'session-pascal'
38
+ });
39
+
40
+ const id1 = headers1.get('mcp-session-id') || headers1.get('Mcp-Session-Id');
41
+ const id2 = headers2.get('mcp-session-id') || headers2.get('Mcp-Session-Id');
42
+
43
+ expect(id1).toBe('session-lower');
44
+ expect(id2).toBe('session-pascal');
45
+ });
46
+
47
+ test('should NOT create ephemeral session for every tool call', () => {
48
+ // Mock multiple tool calls with same session
49
+ const sessionId = 'persistent-session-456';
50
+ const timestamps: string[] = [];
51
+
52
+ for (let i = 0; i < 5; i++) {
53
+ // Simulate tool calls with same session
54
+ const headers = new Headers({
55
+ 'mcp-session-id': sessionId
56
+ });
57
+
58
+ const retrievedId = headers.get('mcp-session-id') ||
59
+ `ephemeral-${Date.now()}-${Math.random()}`;
60
+
61
+ timestamps.push(retrievedId);
62
+ }
63
+
64
+ // All should be the same session ID
65
+ expect(new Set(timestamps).size).toBe(1);
66
+ expect(timestamps[0]).toBe(sessionId);
67
+ });
68
+
69
+ test('should create ephemeral session only when header missing', () => {
70
+ const headers = new Headers({}); // No session ID
71
+
72
+ const sessionId = headers.get('mcp-session-id') ||
73
+ headers.get('Mcp-Session-Id') ||
74
+ `ephemeral-${Date.now()}-${Math.random().toString(36).substring(2, 10)}`;
75
+
76
+ expect(sessionId).toMatch(/^ephemeral-\d+-[a-z0-9]+$/);
77
+ });
78
+ });
79
+
80
+ describe('Session Storage', () => {
81
+ test('should store session with 30-minute TTL per MCP spec', async () => {
82
+ const sessionId = 'test-session-789';
83
+ const sessionData = {
84
+ id: sessionId,
85
+ audience: 'https://kya.vouched.id',
86
+ agentDid: 'did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK',
87
+ createdAt: Date.now(),
88
+ expiresAt: Date.now() + (30 * 60 * 1000) // 30 minutes
89
+ };
90
+
91
+ await mockSessionStorage.put(
92
+ `session:${sessionId}`,
93
+ JSON.stringify(sessionData),
94
+ { expirationTtl: 1800 } // 30 minutes in seconds
95
+ );
96
+
97
+ expect(mockSessionStorage.put).toHaveBeenCalledWith(
98
+ `session:${sessionId}`,
99
+ expect.any(String),
100
+ { expirationTtl: 1800 }
101
+ );
102
+ });
103
+
104
+ test('should validate session TTL on retrieval', async () => {
105
+ const sessionId = 'expired-session';
106
+ const expiredSession = {
107
+ id: sessionId,
108
+ expiresAt: Date.now() - 1000 // Expired 1 second ago
109
+ };
110
+
111
+ mockSessionStorage.get.mockResolvedValueOnce(JSON.stringify(expiredSession));
112
+
113
+ const data = await mockSessionStorage.get(`session:${sessionId}`);
114
+ const session = JSON.parse(data);
115
+
116
+ // Should detect expiration
117
+ const isExpired = session.expiresAt < Date.now();
118
+ expect(isExpired).toBe(true);
119
+
120
+ // Should delete expired session
121
+ if (isExpired) {
122
+ await mockSessionStorage.delete(`session:${sessionId}`);
123
+ expect(mockSessionStorage.delete).toHaveBeenCalled();
124
+ }
125
+ });
126
+
127
+ test('should include delegation token in session when available', async () => {
128
+ const sessionId = 'session-with-delegation';
129
+ const delegationToken = 'jwt-delegation-token-xyz';
130
+
131
+ const sessionData = {
132
+ id: sessionId,
133
+ audience: 'https://kya.vouched.id',
134
+ agentDid: 'did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK',
135
+ createdAt: Date.now(),
136
+ expiresAt: Date.now() + (30 * 60 * 1000),
137
+ delegationToken // Include delegation token
138
+ };
139
+
140
+ await mockSessionStorage.put(
141
+ `session:${sessionId}`,
142
+ JSON.stringify(sessionData),
143
+ { expirationTtl: 1800 }
144
+ );
145
+
146
+ const stored = mockSessionStorage.put.mock.calls[0][1];
147
+ const parsedSession = JSON.parse(stored);
148
+
149
+ expect(parsedSession.delegationToken).toBe(delegationToken);
150
+ });
151
+ });
152
+
153
+ describe('Session Consistency', () => {
154
+ test('should maintain session across multiple tool calls', async () => {
155
+ const sessionId = 'consistent-session-abc';
156
+ const toolCalls = [
157
+ { tool: 'greet', timestamp: Date.now() },
158
+ { tool: 'search', timestamp: Date.now() + 1000 },
159
+ { tool: 'analyze', timestamp: Date.now() + 2000 }
160
+ ];
161
+
162
+ // All calls should use same session
163
+ for (const call of toolCalls) {
164
+ const headers = new Headers({
165
+ 'mcp-session-id': sessionId
166
+ });
167
+
168
+ const retrievedId = headers.get('mcp-session-id');
169
+ expect(retrievedId).toBe(sessionId);
170
+ }
171
+ });
172
+
173
+ test('should handle session rotation gracefully', async () => {
174
+ const oldSessionId = 'old-session-123';
175
+ const newSessionId = 'new-session-456';
176
+
177
+ // Store data with old session
178
+ const oldSessionData = {
179
+ id: oldSessionId,
180
+ data: 'user-specific-data'
181
+ };
182
+
183
+ await mockSessionStorage.put(
184
+ `session:${oldSessionId}`,
185
+ JSON.stringify(oldSessionData)
186
+ );
187
+
188
+ // Rotate to new session
189
+ const newSessionData = {
190
+ id: newSessionId,
191
+ data: 'user-specific-data',
192
+ previousSession: oldSessionId
193
+ };
194
+
195
+ await mockSessionStorage.put(
196
+ `session:${newSessionId}`,
197
+ JSON.stringify(newSessionData)
198
+ );
199
+
200
+ // Verify rotation tracked
201
+ const newStored = mockSessionStorage.put.mock.calls[1][1];
202
+ const parsed = JSON.parse(newStored);
203
+ expect(parsed.previousSession).toBe(oldSessionId);
204
+ });
205
+
206
+ test('should isolate sessions between users', async () => {
207
+ const user1SessionId = 'user1-session';
208
+ const user2SessionId = 'user2-session';
209
+
210
+ const user1Data = {
211
+ id: user1SessionId,
212
+ userId: 'user-1',
213
+ delegationToken: 'user1-delegation'
214
+ };
215
+
216
+ const user2Data = {
217
+ id: user2SessionId,
218
+ userId: 'user-2',
219
+ delegationToken: 'user2-delegation'
220
+ };
221
+
222
+ await mockSessionStorage.put(
223
+ `session:${user1SessionId}`,
224
+ JSON.stringify(user1Data)
225
+ );
226
+
227
+ await mockSessionStorage.put(
228
+ `session:${user2SessionId}`,
229
+ JSON.stringify(user2Data)
230
+ );
231
+
232
+ // Sessions should be completely isolated
233
+ mockSessionStorage.get.mockResolvedValueOnce(JSON.stringify(user1Data));
234
+ const retrieved1 = await mockSessionStorage.get(`session:${user1SessionId}`);
235
+ const session1 = JSON.parse(retrieved1);
236
+
237
+ mockSessionStorage.get.mockResolvedValueOnce(JSON.stringify(user2Data));
238
+ const retrieved2 = await mockSessionStorage.get(`session:${user2SessionId}`);
239
+ const session2 = JSON.parse(retrieved2);
240
+
241
+ expect(session1.delegationToken).not.toBe(session2.delegationToken);
242
+ expect(session1.userId).not.toBe(session2.userId);
243
+ });
244
+ });
245
+
246
+ describe('Session Security', () => {
247
+ test('should not expose session data in logs', () => {
248
+ const consoleLogSpy = vi.spyOn(console, 'log');
249
+
250
+ const sessionId = 'secure-session';
251
+ const sensitiveData = {
252
+ delegationToken: 'secret-token-xyz',
253
+ privateKey: 'private-key-abc'
254
+ };
255
+
256
+ // Simulate logging
257
+ console.log('[Session] Created session:', sessionId);
258
+ // Should NOT log sensitive data
259
+
260
+ expect(consoleLogSpy).toHaveBeenCalledWith(
261
+ '[Session] Created session:',
262
+ sessionId
263
+ );
264
+ expect(consoleLogSpy).not.toHaveBeenCalledWith(
265
+ expect.stringContaining('secret-token')
266
+ );
267
+ expect(consoleLogSpy).not.toHaveBeenCalledWith(
268
+ expect.stringContaining('private-key')
269
+ );
270
+
271
+ consoleLogSpy.mockRestore();
272
+ });
273
+
274
+ test('should validate session audience', async () => {
275
+ const sessionId = 'audience-test-session';
276
+ const sessionData = {
277
+ id: sessionId,
278
+ audience: 'https://kya.vouched.id',
279
+ createdAt: Date.now()
280
+ };
281
+
282
+ // Validate audience matches expected
283
+ expect(sessionData.audience).toBe('https://kya.vouched.id');
284
+
285
+ // Should reject mismatched audience
286
+ const invalidAudience = 'https://evil.com';
287
+ expect(invalidAudience).not.toBe(sessionData.audience);
288
+ });
289
+
290
+ test('should enforce session TTL limits', () => {
291
+ const maxTTL = 30 * 60 * 1000; // 30 minutes
292
+ const timestamp = Date.now();
293
+
294
+ const sessionData = {
295
+ createdAt: timestamp,
296
+ expiresAt: timestamp + maxTTL
297
+ };
298
+
299
+ const ttl = sessionData.expiresAt - sessionData.createdAt;
300
+ expect(ttl).toBeLessThanOrEqual(maxTTL);
301
+ expect(ttl).toBe(30 * 60 * 1000);
302
+ });
303
+ });
304
+
305
+ describe('Edge Cases', () => {
306
+ test('should handle missing session storage gracefully', async () => {
307
+ const env = {}; // No session storage configured
308
+
309
+ // Should not throw, just return null
310
+ const sessionId = 'test-session';
311
+ const result = await Promise.resolve(null); // Would be getDelegationToken result
312
+
313
+ expect(result).toBeNull();
314
+ });
315
+
316
+ test('should handle corrupt session data', async () => {
317
+ const sessionId = 'corrupt-session';
318
+
319
+ // Return invalid JSON
320
+ mockSessionStorage.get.mockResolvedValueOnce('{ invalid json }');
321
+
322
+ try {
323
+ const data = await mockSessionStorage.get(`session:${sessionId}`);
324
+ JSON.parse(data);
325
+ } catch (error) {
326
+ expect(error).toBeInstanceOf(SyntaxError);
327
+ }
328
+ });
329
+
330
+ test('should handle very long session IDs', () => {
331
+ const longSessionId = 'a'.repeat(1000);
332
+ const headers = new Headers({
333
+ 'mcp-session-id': longSessionId
334
+ });
335
+
336
+ const retrievedId = headers.get('mcp-session-id');
337
+ expect(retrievedId).toBe(longSessionId);
338
+ expect(retrievedId.length).toBe(1000);
339
+ });
340
+
341
+ test('should handle concurrent session operations', async () => {
342
+ const sessionId = 'concurrent-session';
343
+
344
+ // Simulate concurrent read/write
345
+ const operations = Array(10).fill(null).map((_, i) =>
346
+ mockSessionStorage.put(
347
+ `session:${sessionId}`,
348
+ JSON.stringify({ counter: i }),
349
+ { expirationTtl: 1800 }
350
+ )
351
+ );
352
+
353
+ await Promise.all(operations);
354
+
355
+ // All operations should complete
356
+ expect(mockSessionStorage.put).toHaveBeenCalledTimes(10);
357
+ });
358
+ });
359
+ });
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ES2022",
5
+ "lib": [
6
+ "ES2022"
7
+ ],
8
+ "types": [
9
+ "@cloudflare/workers-types"
10
+ ],
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "allowSyntheticDefaultImports": true,
14
+ "esModuleInterop": true,
15
+ "strict": true,
16
+ "skipLibCheck": true,
17
+ "forceConsistentCasingInFileNames": true
18
+ },
19
+ "include": [
20
+ "src/**/*"
21
+ ]
22
+ }
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: 'node',
7
+ testTimeout: 30000,
8
+ },
9
+ });
@@ -0,0 +1,37 @@
1
+ #:schema node_modules/wrangler/config-schema.json
2
+ name = "test-cloudflare"
3
+ main = "src/index.ts"
4
+ compatibility_date = "2025-06-18"
5
+ compatibility_flags = ["nodejs_compat"]
6
+
7
+ [[durable_objects.bindings]]
8
+ name = "MCP_OBJECT"
9
+ class_name = "CloudflareMCP"
10
+
11
+ [[migrations]]
12
+ tag = "v1"
13
+ new_sqlite_classes = ["CloudflareMCP"]
14
+
15
+ [[kv_namespaces]]
16
+ binding = "NONCE_CACHE"
17
+ id = "your-kv-namespace-id"
18
+
19
+ [[kv_namespaces]]
20
+ binding = "PROOF_ARCHIVE"
21
+ id = "your-proof-archive-kv-id"
22
+
23
+ [vars]
24
+ XMCP_I_TS_SKEW_SEC = "120"
25
+ XMCP_I_SESSION_TTL = "1800"
26
+
27
+ [env.production]
28
+ name = "test-cloudflare-production"
29
+ vars = { XMCP_I_TS_SKEW_SEC = "60" }
30
+
31
+ [[env.production.kv_namespaces]]
32
+ binding = "NONCE_CACHE"
33
+ id = "your-production-nonce-kv-id"
34
+
35
+ [[env.production.kv_namespaces]]
36
+ binding = "PROOF_ARCHIVE"
37
+ id = "your-production-proof-kv-id"
@@ -0,0 +1,44 @@
1
+ # test-node
2
+
3
+ MCP-I server with cryptographic identity built-in.
4
+
5
+ ## Getting Started
6
+
7
+ 1. Install dependencies:
8
+ ```bash
9
+ npm install
10
+ ```
11
+
12
+ 2. Initialize identity (generates cryptographic keys):
13
+ ```bash
14
+ npm run init
15
+ ```
16
+
17
+ 3. Start development server:
18
+ ```bash
19
+ npm run dev
20
+ ```
21
+
22
+ ## Scripts
23
+
24
+ - `npm run dev` - Start development server with hot reload
25
+ - `npm run build` - Build production bundle
26
+ - `npm run start` - Start production server
27
+ - `npm run init` - Initialize agent identity
28
+ - `npm run status` - Check identity and server status
29
+ - `npm run keys:rotate` - Rotate cryptographic keys
30
+
31
+ ## Project Structure
32
+
33
+ ```
34
+ ├── src/
35
+ │ └── tools/ # MCP tools
36
+ │ └── greet.ts # Example tool
37
+ ├── xmcp.config.ts # MCP-I configuration
38
+ └── .mcpi/ # Identity files (git-ignored)
39
+ ```
40
+
41
+ ## Learn More
42
+
43
+ - [MCP-I Documentation](https://github.com/modelcontextprotocol-identity/mcp-i)
44
+ - [Model Context Protocol](https://modelcontextprotocol.io)
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "test-node",
3
+ "version": "0.1.0",
4
+ "dependencies": {
5
+ "@kya-os/cli": "^1.2.7",
6
+ "@kya-os/mcp-i": "^1.2.7",
7
+ "zod": "^3.24.4"
8
+ },
9
+ "scripts": {
10
+ "dev": "mcpi dev",
11
+ "build": "mcpi build",
12
+ "start": "mcpi start",
13
+ "init": "mcpi init",
14
+ "register": "mcpi register",
15
+ "keys:rotate": "mcpi rotate",
16
+ "identity:clean": "rm -rf .mcpi",
17
+ "status": "mcpi status"
18
+ },
19
+ "devDependencies": {
20
+ "@modelcontextprotocol/inspector": "^0.16.6",
21
+ "swc-loader": "^0.2.6"
22
+ }
23
+ }
@@ -0,0 +1,25 @@
1
+ import { z } from "zod";
2
+ import { type InferSchema, type ToolMetadata } from "@kya-os/mcp-i";
3
+
4
+ // Define the schema for tool parameters
5
+ export const schema = {
6
+ name: z.string().describe("The name of the user to greet"),
7
+ };
8
+
9
+ // Define tool metadata
10
+ export const metadata: ToolMetadata = {
11
+ name: "greet",
12
+ description: "Greet the user",
13
+ };
14
+
15
+ // Tool implementation
16
+ export default async function greet({ name }: InferSchema<typeof schema>) {
17
+ return {
18
+ content: [
19
+ {
20
+ type: "text" as const,
21
+ text: `Hello, ${name}!`,
22
+ },
23
+ ],
24
+ };
25
+ }
@@ -0,0 +1,20 @@
1
+ import type { XmcpConfig } from "@kya-os/mcp-i";
2
+
3
+ const config: XmcpConfig = {
4
+ // Point to the tools directory
5
+ paths: {
6
+ tools: "./src/tools",
7
+ },
8
+
9
+ // Enable MCP-I identity features
10
+ identity: {
11
+ enabled: true,
12
+ environment: "development",
13
+ debug: true,
14
+ },
15
+
16
+ // Enable STDIO transport for Claude Desktop
17
+ stdio: true,
18
+ };
19
+
20
+ export default config;
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ES2022",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ES2022"],
7
+ "outDir": "./dist",
8
+ "rootDir": "./src",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "declaration": true,
14
+ "declarationMap": true,
15
+ "sourceMap": true,
16
+ "resolveJsonModule": true,
17
+ "allowSyntheticDefaultImports": true,
18
+ "allowJs": false,
19
+ "noEmit": false,
20
+ "incremental": true,
21
+ "tsBuildInfoFile": "./dist/.tsbuildinfo"
22
+ },
23
+ "include": ["src/**/*"],
24
+ "exclude": ["node_modules", "dist", "templates", "src/**/*.test.ts", "src/**/__tests__/**", "src/__tests__", "src/__tests__.bak"],
25
+ "references": [{ "path": "../contracts" }]
26
+ }
@@ -0,0 +1,14 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ exclude: [
6
+ '**/node_modules/**',
7
+ '**/dist/**',
8
+ '**/.{idea,git,cache,output,temp}/**',
9
+ '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*',
10
+ '**/__tests__.bak/**', // Exclude backup tests
11
+ '**/integration.test.ts', // Exclude Wrangler integration tests (requires wrangler dependency)
12
+ ],
13
+ },
14
+ });