mcp-http-webhook 1.0.0
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/.eslintrc.json +16 -0
- package/.prettierrc.json +8 -0
- package/ARCHITECTURE.md +269 -0
- package/CONTRIBUTING.md +136 -0
- package/GETTING_STARTED.md +310 -0
- package/IMPLEMENTATION.md +294 -0
- package/LICENSE +21 -0
- package/MIGRATION_TO_SDK.md +263 -0
- package/README.md +496 -0
- package/SDK_INTEGRATION_COMPLETE.md +300 -0
- package/STANDARD_SUBSCRIPTIONS.md +268 -0
- package/STANDARD_SUBSCRIPTIONS_COMPLETE.md +309 -0
- package/SUMMARY.md +272 -0
- package/Spec.md +2778 -0
- package/dist/errors/index.d.ts +52 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +81 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/ProtocolHandler.d.ts +37 -0
- package/dist/protocol/ProtocolHandler.d.ts.map +1 -0
- package/dist/protocol/ProtocolHandler.js +172 -0
- package/dist/protocol/ProtocolHandler.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +502 -0
- package/dist/server.js.map +1 -0
- package/dist/stores/InMemoryStore.d.ts +27 -0
- package/dist/stores/InMemoryStore.d.ts.map +1 -0
- package/dist/stores/InMemoryStore.js +73 -0
- package/dist/stores/InMemoryStore.js.map +1 -0
- package/dist/stores/RedisStore.d.ts +18 -0
- package/dist/stores/RedisStore.d.ts.map +1 -0
- package/dist/stores/RedisStore.js +45 -0
- package/dist/stores/RedisStore.js.map +1 -0
- package/dist/stores/index.d.ts +3 -0
- package/dist/stores/index.d.ts.map +1 -0
- package/dist/stores/index.js +9 -0
- package/dist/stores/index.js.map +1 -0
- package/dist/subscriptions/SubscriptionManager.d.ts +49 -0
- package/dist/subscriptions/SubscriptionManager.d.ts.map +1 -0
- package/dist/subscriptions/SubscriptionManager.js +181 -0
- package/dist/subscriptions/SubscriptionManager.js.map +1 -0
- package/dist/types/index.d.ts +271 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +16 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/index.d.ts +51 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +154 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/webhooks/WebhookManager.d.ts +27 -0
- package/dist/webhooks/WebhookManager.d.ts.map +1 -0
- package/dist/webhooks/WebhookManager.js +174 -0
- package/dist/webhooks/WebhookManager.js.map +1 -0
- package/examples/GITHUB_LIVE_EXAMPLE.md +308 -0
- package/examples/GITHUB_LIVE_SETUP.md +253 -0
- package/examples/QUICKSTART.md +130 -0
- package/examples/basic-setup.ts +142 -0
- package/examples/github-server-live.ts +690 -0
- package/examples/github-server.ts +223 -0
- package/examples/google-drive-server-live.ts +773 -0
- package/examples/start-github-live.sh +53 -0
- package/jest.config.js +20 -0
- package/package.json +58 -0
- package/src/errors/index.ts +81 -0
- package/src/index.ts +19 -0
- package/src/server.ts +595 -0
- package/src/stores/InMemoryStore.ts +87 -0
- package/src/stores/RedisStore.ts +51 -0
- package/src/stores/index.ts +2 -0
- package/src/subscriptions/SubscriptionManager.ts +240 -0
- package/src/types/index.ts +341 -0
- package/src/utils/index.ts +156 -0
- package/src/webhooks/WebhookManager.ts +230 -0
- package/test-sdk-integration.sh +157 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { createMCPServer } from '../src';
|
|
2
|
+
import { InMemoryStore } from '../src/stores';
|
|
3
|
+
import crypto from 'crypto';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* GitHub MCP Server Example
|
|
7
|
+
* Demonstrates webhook-based subscriptions
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const store = new InMemoryStore();
|
|
11
|
+
|
|
12
|
+
const server = createMCPServer({
|
|
13
|
+
name: 'github-mcp',
|
|
14
|
+
version: '1.0.0',
|
|
15
|
+
publicUrl: process.env.PUBLIC_URL || 'https://mcp.example.com',
|
|
16
|
+
port: 3000,
|
|
17
|
+
store,
|
|
18
|
+
|
|
19
|
+
tools: [
|
|
20
|
+
{
|
|
21
|
+
name: 'create_issue',
|
|
22
|
+
description: 'Create a GitHub issue',
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
owner: { type: 'string', description: 'Repository owner' },
|
|
27
|
+
repo: { type: 'string', description: 'Repository name' },
|
|
28
|
+
title: { type: 'string', description: 'Issue title' },
|
|
29
|
+
body: { type: 'string', description: 'Issue body' },
|
|
30
|
+
},
|
|
31
|
+
required: ['owner', 'repo', 'title'],
|
|
32
|
+
},
|
|
33
|
+
handler: async (input, context) => {
|
|
34
|
+
// Simulate GitHub API call
|
|
35
|
+
console.log('Creating issue:', input);
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
issue: {
|
|
39
|
+
number: 42,
|
|
40
|
+
title: input.title,
|
|
41
|
+
state: 'open',
|
|
42
|
+
html_url: `https://github.com/${input.owner}/${input.repo}/issues/42`,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
|
|
49
|
+
resources: [
|
|
50
|
+
{
|
|
51
|
+
uri: 'github://repo/{owner}/{repo}/issues',
|
|
52
|
+
name: 'GitHub Repository Issues',
|
|
53
|
+
description: 'All issues in a GitHub repository',
|
|
54
|
+
mimeType: 'application/json',
|
|
55
|
+
|
|
56
|
+
read: async (uri, context) => {
|
|
57
|
+
const parts = uri.split('/');
|
|
58
|
+
const owner = parts[3];
|
|
59
|
+
const repo = parts[4];
|
|
60
|
+
|
|
61
|
+
console.log(`Reading issues for ${owner}/${repo}`);
|
|
62
|
+
|
|
63
|
+
// Simulate GitHub API call
|
|
64
|
+
return {
|
|
65
|
+
contents: [
|
|
66
|
+
{
|
|
67
|
+
number: 1,
|
|
68
|
+
title: 'Example issue',
|
|
69
|
+
state: 'open',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
list: async (context) => {
|
|
76
|
+
return [
|
|
77
|
+
{
|
|
78
|
+
uri: 'github://repo/octocat/hello-world/issues',
|
|
79
|
+
name: 'octocat/hello-world Issues',
|
|
80
|
+
description: 'Issues for hello-world repository',
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
subscription: {
|
|
86
|
+
onSubscribe: async (uri, subscriptionId, thirdPartyWebhookUrl, context) => {
|
|
87
|
+
console.log('Subscribing to:', uri);
|
|
88
|
+
console.log('Third-party webhook URL:', thirdPartyWebhookUrl);
|
|
89
|
+
|
|
90
|
+
// In production, this would:
|
|
91
|
+
// 1. Call GitHub API to create webhook
|
|
92
|
+
// 2. Store webhook ID
|
|
93
|
+
// Example:
|
|
94
|
+
// const webhook = await octokit.repos.createWebhook({
|
|
95
|
+
// owner,
|
|
96
|
+
// repo,
|
|
97
|
+
// config: {
|
|
98
|
+
// url: thirdPartyWebhookUrl,
|
|
99
|
+
// content_type: 'json',
|
|
100
|
+
// secret: process.env.GITHUB_WEBHOOK_SECRET
|
|
101
|
+
// },
|
|
102
|
+
// events: ['issues']
|
|
103
|
+
// });
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
thirdPartyWebhookId: 'webhook_123',
|
|
107
|
+
metadata: {
|
|
108
|
+
owner: 'octocat',
|
|
109
|
+
repo: 'hello-world',
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
onUnsubscribe: async (uri, subscriptionId, storedData, context) => {
|
|
115
|
+
console.log('Unsubscribing from:', uri);
|
|
116
|
+
console.log('Removing webhook:', storedData.thirdPartyWebhookId);
|
|
117
|
+
|
|
118
|
+
// In production, this would:
|
|
119
|
+
// await octokit.repos.deleteWebhook({
|
|
120
|
+
// owner,
|
|
121
|
+
// repo,
|
|
122
|
+
// hook_id: storedData.thirdPartyWebhookId
|
|
123
|
+
// });
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
onWebhook: async (subscriptionId, payload, headers) => {
|
|
127
|
+
console.log('Received GitHub webhook:', subscriptionId);
|
|
128
|
+
|
|
129
|
+
// Verify signature
|
|
130
|
+
const signature = headers['x-hub-signature-256'];
|
|
131
|
+
if (signature) {
|
|
132
|
+
// Verification logic here
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const event = headers['x-github-event'];
|
|
136
|
+
|
|
137
|
+
if (event === 'issues') {
|
|
138
|
+
const { action, issue, repository } = payload;
|
|
139
|
+
|
|
140
|
+
if (['opened', 'edited', 'closed'].includes(action)) {
|
|
141
|
+
return {
|
|
142
|
+
resourceUri: `github://repo/${repository.owner.login}/${repository.name}/issues`,
|
|
143
|
+
changeType: action === 'opened' ? 'created' : action === 'closed' ? 'deleted' : 'updated',
|
|
144
|
+
data: {
|
|
145
|
+
issueNumber: issue.number,
|
|
146
|
+
title: issue.title,
|
|
147
|
+
state: issue.state,
|
|
148
|
+
action,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return null;
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
|
|
160
|
+
webhooks: {
|
|
161
|
+
incomingPath: '/webhooks/incoming',
|
|
162
|
+
incomingSecret: process.env.GITHUB_WEBHOOK_SECRET,
|
|
163
|
+
|
|
164
|
+
verifyIncomingSignature: (payload, signature, secret) => {
|
|
165
|
+
const hmac = crypto.createHmac('sha256', secret);
|
|
166
|
+
hmac.update(JSON.stringify(payload));
|
|
167
|
+
const expected = `sha256=${hmac.digest('hex')}`;
|
|
168
|
+
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
outgoing: {
|
|
172
|
+
timeout: 5000,
|
|
173
|
+
retries: 3,
|
|
174
|
+
retryDelay: 1000,
|
|
175
|
+
|
|
176
|
+
signPayload: (payload, secret) => {
|
|
177
|
+
const hmac = crypto.createHmac('sha256', secret);
|
|
178
|
+
hmac.update(JSON.stringify(payload));
|
|
179
|
+
return `sha256=${hmac.digest('hex')}`;
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
authenticate: async (req) => {
|
|
185
|
+
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
186
|
+
if (!token) {
|
|
187
|
+
throw new Error('Missing authorization token');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// In production, verify JWT token
|
|
191
|
+
return {
|
|
192
|
+
userId: 'user-123',
|
|
193
|
+
githubToken: token,
|
|
194
|
+
};
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
logLevel: 'debug',
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
server.start().then(() => {
|
|
201
|
+
console.log('GitHub MCP Server running!');
|
|
202
|
+
console.log('');
|
|
203
|
+
console.log('Subscribe to issues:');
|
|
204
|
+
console.log('POST http://localhost:3000/mcp/resources/subscribe');
|
|
205
|
+
console.log(' Body:');
|
|
206
|
+
console.log(' {');
|
|
207
|
+
console.log(' "method": "resources/subscribe",');
|
|
208
|
+
console.log(' "params": {');
|
|
209
|
+
console.log(' "uri": "github://repo/octocat/hello-world/issues",');
|
|
210
|
+
console.log(' "callbackUrl": "https://client.example.com/webhook",');
|
|
211
|
+
console.log(' "callbackSecret": "client-secret"');
|
|
212
|
+
console.log(' }');
|
|
213
|
+
console.log(' }');
|
|
214
|
+
console.log('');
|
|
215
|
+
console.log('Webhook endpoint:');
|
|
216
|
+
console.log(`${process.env.PUBLIC_URL}/webhooks/incoming/{subscriptionId}`);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
process.on('SIGTERM', async () => {
|
|
220
|
+
await server.stop();
|
|
221
|
+
store.destroy();
|
|
222
|
+
process.exit(0);
|
|
223
|
+
});
|