Merge pull request #19 from SootyOwl/refactor/streamline-server
Streamline and unify server creation, self-documenting tools.
This commit is contained in:
commit
ef0391f862
|
|
@ -19,7 +19,7 @@ RUN npm run build
|
|||
# Expose HTTP port
|
||||
EXPOSE 8080
|
||||
|
||||
# entrypoint to run the MCP server
|
||||
ENTRYPOINT ["node", "build/app.js"]
|
||||
# Default command to run the MCP server with HTTP transport
|
||||
# Use ENTRYPOINT + CMD for flexibility
|
||||
ENTRYPOINT ["node", "build/index.js"]
|
||||
CMD ["--transport", "stdio", "--port", "8080"]
|
||||
CMD ["--transport", "http", "--port", "8080"]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
.PHONY: help
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
# initialise .env file for all targets if it exists
|
||||
-include .env
|
||||
|
||||
help: ## Show this help.
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
# Document each target that should appear in help using a comment placed after the target's colon, starting with '##' (as shown above).
|
||||
# Targets without such a comment will not appear in the help output.
|
||||
|
||||
run-inspector: ## Run the MCP inspector.
|
||||
@npx @modelcontextprotocol/inspector -e DISCORD_TOKEN=$(DISCORD_TOKEN)
|
||||
|
|
@ -293,6 +293,9 @@ You can use Docker containers with both Claude and Cursor:
|
|||
|
||||
- `discord_create_text_channel`: Create a text channel
|
||||
- `discord_delete_channel`: Delete a channel
|
||||
- `discord_create_category`: Create a channel category
|
||||
- `discord_edit_category`: Edit a channel category
|
||||
- `discord_delete_category`: Delete a channel category
|
||||
|
||||
### Forum Functions
|
||||
|
||||
|
|
|
|||
|
|
@ -42,15 +42,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@discordjs/builders": {
|
||||
"version": "1.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.11.2.tgz",
|
||||
"integrity": "sha512-F1WTABdd8/R9D1icJzajC4IuLyyS8f3rTOz66JsSI3pKvpCAtsMBweu8cyNYsIyvcrKAVn9EPK+Psoymq+XC0A==",
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.12.2.tgz",
|
||||
"integrity": "sha512-AugKmrgRJOHXEyMkABH/hXpAmIBKbYokCEl9VAM4Kh6FvkdobQ+Zhv7AR6K+y5hS7+VQ7gKXPYCe1JQmV07H1g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@discordjs/formatters": "^0.6.1",
|
||||
"@discordjs/util": "^1.1.1",
|
||||
"@sapphire/shapeshift": "^4.0.0",
|
||||
"discord-api-types": "^0.38.1",
|
||||
"discord-api-types": "^0.38.26",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"ts-mixer": "^6.0.4",
|
||||
"tslib": "^2.6.3"
|
||||
|
|
@ -87,9 +87,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@discordjs/rest": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.5.0.tgz",
|
||||
"integrity": "sha512-PWhchxTzpn9EV3vvPRpwS0EE2rNYB9pvzDU/eLLW3mByJl0ZHZjHI2/wA8EbH2gRMQV7nu+0FoDF84oiPl8VAQ==",
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.6.0.tgz",
|
||||
"integrity": "sha512-RDYrhmpB7mTvmCKcpj+pc5k7POKszS4E2O9TYc+U+Y4iaCP+r910QdO43qmpOja8LRr1RJ0b3U+CqVsnPqzf4w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@discordjs/collection": "^2.1.1",
|
||||
|
|
@ -97,10 +97,10 @@
|
|||
"@sapphire/async-queue": "^1.5.3",
|
||||
"@sapphire/snowflake": "^3.5.3",
|
||||
"@vladfrangu/async_event_emitter": "^2.4.6",
|
||||
"discord-api-types": "^0.38.1",
|
||||
"discord-api-types": "^0.38.16",
|
||||
"magic-bytes.js": "^1.10.0",
|
||||
"tslib": "^2.6.3",
|
||||
"undici": "6.21.1"
|
||||
"undici": "6.21.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
|
@ -134,13 +134,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@discordjs/ws": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.2.2.tgz",
|
||||
"integrity": "sha512-dyfq7yn0wO0IYeYOs3z79I6/HumhmKISzFL0Z+007zQJMtAFGtt3AEoq1nuLXtcunUE5YYYQqgKvybXukAK8/w==",
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.2.3.tgz",
|
||||
"integrity": "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@discordjs/collection": "^2.1.0",
|
||||
"@discordjs/rest": "^2.5.0",
|
||||
"@discordjs/rest": "^2.5.1",
|
||||
"@discordjs/util": "^1.1.0",
|
||||
"@sapphire/async-queue": "^1.5.2",
|
||||
"@types/ws": "^8.5.10",
|
||||
|
|
@ -179,9 +179,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
|
||||
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
|
@ -197,15 +197,17 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/sdk": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.0.tgz",
|
||||
"integrity": "sha512-k/1pb70eD638anoi0e8wUGAlbMJXyvdV4p62Ko+EZ7eBe1xMx8Uhak1R5DgfoofsK5IBBnRwsYGTaLZl+6/+RQ==",
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.20.1.tgz",
|
||||
"integrity": "sha512-j/P+yuxXfgxb+mW7OEoRCM3G47zCTDqUPivJo/VzpjbG8I9csTXtOprCf5FfOfHK4whOJny0aHuBEON+kS7CCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.6",
|
||||
"content-type": "^1.0.5",
|
||||
"cors": "^2.8.5",
|
||||
"cross-spawn": "^7.0.3",
|
||||
"cross-spawn": "^7.0.5",
|
||||
"eventsource": "^3.0.2",
|
||||
"eventsource-parser": "^3.0.0",
|
||||
"express": "^5.0.1",
|
||||
"express-rate-limit": "^7.5.0",
|
||||
"pkce-challenge": "^5.0.0",
|
||||
|
|
@ -279,9 +281,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/body-parser": {
|
||||
"version": "1.19.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
|
||||
"integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
|
||||
"version": "1.19.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
|
||||
"integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -300,9 +302,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.1.tgz",
|
||||
"integrity": "sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==",
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz",
|
||||
"integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -312,9 +314,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/express-serve-static-core": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz",
|
||||
"integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==",
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz",
|
||||
"integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -325,9 +327,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/http-errors": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
|
||||
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
|
||||
"integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
|
@ -339,18 +341,19 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.17.50",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.50.tgz",
|
||||
"integrity": "sha512-Mxiq0ULv/zo1OzOhwPqOA13I81CV/W3nvd3ChtQZRT5Cwz3cr0FKo/wMSsbTqL3EXpaBAEQhva2B8ByRkOIh9A==",
|
||||
"version": "20.19.22",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.22.tgz",
|
||||
"integrity": "sha512-hRnu+5qggKDSyWHlnmThnUqg62l29Aj/6vcYgUaSFL9oc7DVjeWEQN3PRgdSc6F8d9QRMWkf36CLMch1Do/+RQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
"version": "6.9.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz",
|
||||
"integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==",
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
|
||||
"integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
|
@ -362,26 +365,36 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/send": {
|
||||
"version": "0.17.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
|
||||
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.0.tgz",
|
||||
"integrity": "sha512-zBF6vZJn1IaMpg3xUF25VK3gd3l8zwE0ZLRX7dsQyQi+jp4E8mMDJNGDYnYse+bQhYwWERTxVwHpi3dMOq7RKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mime": "^1",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/serve-static": {
|
||||
"version": "1.15.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz",
|
||||
"integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.9.tgz",
|
||||
"integrity": "sha512-dOTIuqpWLyl3BBXU3maNQsS4A3zuuoYRNIvYSxxhebPfXg2mzWQEPne/nlJ37yOse6uGgR386uTpdsx4D0QZWA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/http-errors": "*",
|
||||
"@types/node": "*",
|
||||
"@types/send": "*"
|
||||
"@types/send": "<1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/serve-static/node_modules/@types/send": {
|
||||
"version": "0.17.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz",
|
||||
"integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mime": "^1",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
|
|
@ -394,9 +407,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vladfrangu/async_event_emitter": {
|
||||
"version": "2.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz",
|
||||
"integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==",
|
||||
"version": "2.4.7",
|
||||
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.7.tgz",
|
||||
"integrity": "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=v14.0.0",
|
||||
|
|
@ -417,9 +430,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.14.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
||||
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
|
|
@ -442,6 +455,22 @@
|
|||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
|
|
@ -529,9 +558,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
|
|
@ -581,9 +610,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
|
|
@ -617,33 +646,33 @@
|
|||
}
|
||||
},
|
||||
"node_modules/discord-api-types": {
|
||||
"version": "0.38.8",
|
||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.8.tgz",
|
||||
"integrity": "sha512-xuRXPD44FcbKHrQK15FS1HFlMRNJtsaZou/SVws18vQ7zHqmlxyDktMkZpyvD6gE2ctGOVYC/jUyoMMAyBWfcw==",
|
||||
"version": "0.38.30",
|
||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.30.tgz",
|
||||
"integrity": "sha512-KhAqlBrg+rVK+Ob7INMF5o63yW4/GUzRatG/AjyVsIO8lgcLyR8qCl2HokIVzWwmzkJYG0CEPXsKMOqau3E8NA==",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"scripts/actions/documentation"
|
||||
]
|
||||
},
|
||||
"node_modules/discord.js": {
|
||||
"version": "14.19.3",
|
||||
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.19.3.tgz",
|
||||
"integrity": "sha512-lncTRk0k+8Q5D3nThnODBR8fR8x2fM798o8Vsr40Krx0DjPwpZCuxxTcFMrXMQVOqM1QB9wqWgaXPg3TbmlHqA==",
|
||||
"version": "14.23.2",
|
||||
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.23.2.tgz",
|
||||
"integrity": "sha512-tU2NFr823X3TXEc8KyR/4m296KLxPai4nirN3q9kHCpY4TKj96n9lHZnyLzRNMui8EbL07jg9hgH2PWWfKMGIg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@discordjs/builders": "^1.11.2",
|
||||
"@discordjs/builders": "^1.12.1",
|
||||
"@discordjs/collection": "1.5.3",
|
||||
"@discordjs/formatters": "^0.6.1",
|
||||
"@discordjs/rest": "^2.5.0",
|
||||
"@discordjs/rest": "^2.6.0",
|
||||
"@discordjs/util": "^1.1.1",
|
||||
"@discordjs/ws": "^1.2.2",
|
||||
"@discordjs/ws": "^1.2.3",
|
||||
"@sapphire/snowflake": "3.5.3",
|
||||
"discord-api-types": "^0.38.1",
|
||||
"discord-api-types": "^0.38.29",
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"lodash.snakecase": "4.1.1",
|
||||
"magic-bytes.js": "^1.10.0",
|
||||
"tslib": "^2.6.3",
|
||||
"undici": "6.21.1"
|
||||
"undici": "6.21.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
|
@ -653,9 +682,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.5.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
|
||||
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
|
||||
"version": "16.6.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
||||
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
|
|
@ -739,21 +768,21 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eventsource": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.5.tgz",
|
||||
"integrity": "sha512-LT/5J605bx5SNyE+ITBDiM3FxffBiq9un7Vx0EwMDM3vg8sWKx/tO2zC+LMqZ+smAM0F2hblaDZUVZF0te2pSw==",
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
|
||||
"integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eventsource-parser": "^3.0.0"
|
||||
"eventsource-parser": "^3.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eventsource-parser": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.0.tgz",
|
||||
"integrity": "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==",
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz",
|
||||
"integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
|
|
@ -764,6 +793,7 @@
|
|||
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
|
||||
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.2.0",
|
||||
|
|
@ -802,9 +832,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/express-rate-limit": {
|
||||
"version": "7.5.0",
|
||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz",
|
||||
"integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==",
|
||||
"version": "7.5.1",
|
||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz",
|
||||
"integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
|
|
@ -813,7 +843,7 @@
|
|||
"url": "https://github.com/sponsors/express-rate-limit"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"express": "^4.11 || 5 || ^5.0.0-beta.1"
|
||||
"express": ">= 4.11"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
|
|
@ -822,6 +852,12 @@
|
|||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
||||
|
|
@ -955,6 +991,15 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/http-errors/node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
|
|
@ -994,6 +1039,12 @@
|
|||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
|
|
@ -1146,12 +1197,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
|
||||
"integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
|
||||
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/pkce-challenge": {
|
||||
|
|
@ -1176,6 +1228,15 @@
|
|||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
||||
|
|
@ -1201,18 +1262,34 @@
|
|||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
|
||||
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz",
|
||||
"integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.6.3",
|
||||
"iconv-lite": "0.7.0",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body/node_modules/iconv-lite": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
|
||||
"integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/router": {
|
||||
|
|
@ -1394,9 +1471,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
|
|
@ -1482,11 +1559,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
|
@ -1496,18 +1574,18 @@
|
|||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "6.21.1",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz",
|
||||
"integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==",
|
||||
"version": "6.21.3",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz",
|
||||
"integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.19.8",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
|
|
@ -1519,6 +1597,15 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
|
|
@ -1557,9 +1644,9 @@
|
|||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.18.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
|
||||
"integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
|
|
@ -1588,18 +1675,19 @@
|
|||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.25.13",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.13.tgz",
|
||||
"integrity": "sha512-Q8mvk2iWi7rTDfpQBsu4ziE7A6AxgzJ5hzRyRYQkoV3A3niYsXVwDaP1Kbz3nWav6S+VZ6k2OznFn8ZyDHvIrg==",
|
||||
"version": "3.25.76",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/zod-to-json-schema": {
|
||||
"version": "3.24.5",
|
||||
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz",
|
||||
"integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==",
|
||||
"version": "3.24.6",
|
||||
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz",
|
||||
"integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"zod": "^3.24.1"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@
|
|||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "tsc",
|
||||
"start": "node build/index.js",
|
||||
"start-app": "node build/app.js",
|
||||
"dev": "node --loader ts-node/esm src/index.ts",
|
||||
"dev-app": "node --loader ts-node/esm src/app.ts",
|
||||
"test-api": "node test-api.js",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,344 @@
|
|||
import { InMemoryEventStore } from '@modelcontextprotocol/sdk/examples/shared/inMemoryEventStore.js';
|
||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
||||
import { isInitializeRequest, SetLevelRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
||||
import { randomUUID } from 'crypto';
|
||||
import { Client, GatewayIntentBits } from 'discord.js';
|
||||
import { config as dotenvConfig } from 'dotenv';
|
||||
import express, { Request, Response } from 'express';
|
||||
import { info, Level, setLevel, warning } from './notifications.js';
|
||||
import * as schemas from './schemas.js';
|
||||
import * as handlers from './tools/tools.js';
|
||||
import { createToolContext } from './tools/tools.js';
|
||||
|
||||
// Load environment variables from .env file if exists
|
||||
dotenvConfig();
|
||||
|
||||
/**
|
||||
* Create and return a configured MCP server wired with the Discord client.
|
||||
* The server registers tools programmatically using the handlers and schemas
|
||||
* exported from the local modules to avoid a large switch statement.
|
||||
*/
|
||||
function createMcpServer(client: Client) {
|
||||
const server = new McpServer(
|
||||
{ name: 'discord-mcp-server', version: '1.0.0' },
|
||||
{ capabilities: { tools: {}, logging: {} } }
|
||||
);
|
||||
// attach the Discord client to the tool context for use in handlers
|
||||
const toolContext = createToolContext(client);
|
||||
// attach the Discord client to the server for logging purposes
|
||||
// The underlying Server type from the SDK doesn't declare a `client` property,
|
||||
// so assert to `any` to attach a runtime-only property used by handlers.
|
||||
(client as any).server = server.server;
|
||||
(server as any).discord = client;
|
||||
|
||||
// Map of tool name to handler + schema. Keep this small and explicit so it's
|
||||
// easy to see which handler is wired to which tool. Use the exported
|
||||
// Zod schemas from `schemas.ts` where available.
|
||||
const toolMap: Array<{ name: string; schema?: any; handler: any }> = [
|
||||
{ name: 'discord_login', schema: schemas.DiscordLoginSchema, handler: handlers.loginHandler },
|
||||
{ name: 'discord_list_servers', schema: schemas.ListServersSchema, handler: handlers.listServersHandler },
|
||||
{ name: 'discord_send', schema: schemas.SendMessageSchema, handler: handlers.sendMessageHandler },
|
||||
{ name: 'discord_get_server_info', schema: schemas.GetServerInfoSchema, handler: handlers.getServerInfoHandler },
|
||||
{ name: 'discord_create_text_channel', schema: schemas.CreateTextChannelSchema, handler: handlers.createTextChannelHandler },
|
||||
{ name: 'discord_delete_channel', schema: schemas.DeleteChannelSchema, handler: handlers.deleteChannelHandler },
|
||||
{ name: 'discord_get_forum_channels', schema: schemas.GetForumChannelsSchema, handler: handlers.getForumChannelsHandler },
|
||||
{ name: 'discord_create_forum_post', schema: schemas.CreateForumPostSchema, handler: handlers.createForumPostHandler },
|
||||
{ name: 'discord_get_forum_post', schema: schemas.GetForumPostSchema, handler: handlers.getForumPostHandler },
|
||||
{ name: 'discord_reply_to_forum', schema: schemas.ReplyToForumSchema, handler: handlers.replyToForumHandler },
|
||||
{ name: 'discord_delete_forum_post', schema: schemas.DeleteForumPostSchema, handler: handlers.deleteForumPostHandler },
|
||||
{ name: 'discord_search_messages', schema: schemas.SearchMessagesSchema, handler: handlers.searchMessagesHandler },
|
||||
{ name: 'discord_read_messages', schema: schemas.ReadMessagesSchema, handler: handlers.readMessagesHandler },
|
||||
{ name: 'discord_add_reaction', schema: schemas.AddReactionSchema, handler: handlers.addReactionHandler },
|
||||
{ name: 'discord_add_multiple_reactions', schema: schemas.AddMultipleReactionsSchema, handler: handlers.addMultipleReactionsHandler },
|
||||
{ name: 'discord_remove_reaction', schema: schemas.RemoveReactionSchema, handler: handlers.removeReactionHandler },
|
||||
{ name: 'discord_delete_message', schema: schemas.DeleteMessageSchema, handler: handlers.deleteMessageHandler },
|
||||
{ name: 'discord_create_webhook', schema: schemas.CreateWebhookSchema, handler: handlers.createWebhookHandler },
|
||||
{ name: 'discord_send_webhook_message', schema: schemas.SendWebhookMessageSchema, handler: handlers.sendWebhookMessageHandler },
|
||||
{ name: 'discord_edit_webhook', schema: schemas.EditWebhookSchema, handler: handlers.editWebhookHandler },
|
||||
{ name: 'discord_delete_webhook', schema: schemas.DeleteWebhookSchema, handler: handlers.deleteWebhookHandler },
|
||||
{ name: 'discord_create_category', schema: schemas.CreateCategorySchema, handler: handlers.createCategoryHandler },
|
||||
{ name: 'discord_edit_category', schema: schemas.EditCategorySchema, handler: handlers.editCategoryHandler },
|
||||
{ name: 'discord_delete_category', schema: schemas.DeleteCategorySchema, handler: handlers.deleteCategoryHandler },
|
||||
];
|
||||
|
||||
// Register each tool on the MCP server
|
||||
for (const t of toolMap) {
|
||||
try {
|
||||
server.tool(
|
||||
t.name,
|
||||
t.schema ? t.schema.description ?? '' : '',
|
||||
t.schema ? t.schema.shape ?? t.schema : undefined, async (args: any) => {
|
||||
// Handlers follow the signature: (args, context) => Promise<ToolResponse>
|
||||
return await t.handler(args, toolContext);
|
||||
});
|
||||
} catch (err) {
|
||||
// If a particular tool registration fails, log and continue (keeps boot resilient)
|
||||
// eslint-disable-next-line no-console
|
||||
warning(server.server, `Failed to register tool ${t.name}: ${String(err)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle logging method separately
|
||||
server.server.setRequestHandler(SetLevelRequestSchema, async (request) => {
|
||||
const levelParam = request.params.level as Level | undefined;
|
||||
const ok = setLevel(levelParam || 'info');
|
||||
// Empty result on success, error on failure
|
||||
if (!ok) {
|
||||
throw { code: -32602, message: 'Invalid log level' };
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
// Helper to create a Discord client with sensible defaults used by the CLI
|
||||
function createDiscordClient(token?: string) {
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent] });
|
||||
if (token) {
|
||||
client.token = token;
|
||||
// Auto-login on startup if token is available
|
||||
(async () => {
|
||||
try {
|
||||
await client.login(token);
|
||||
info((client as any).server, 'Successfully logged in to Discord');
|
||||
} catch (err: any) {
|
||||
if (typeof err.message === 'string' && err.message.includes('Privileged intent provided is not enabled or whitelisted')) {
|
||||
warning((client as any).server, 'Discord login failed due to missing privileged intents. Please enable the required intents in the Discord Developer Portal.');
|
||||
} else {
|
||||
warning((client as any).server, 'Discord login failed: ' + String(err));
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
let clientStatusInterval: NodeJS.Timeout | null = null;
|
||||
|
||||
function logClientState(context: string) {
|
||||
// only log if server is attached and connected
|
||||
if (!(client as any).server || !(client as any).server.transport) {
|
||||
return;
|
||||
}
|
||||
info((client as any).server, `Discord client state [${context}]: ${JSON.stringify({
|
||||
isReady: client.isReady(),
|
||||
hasToken: !!client.token,
|
||||
user: client.user ? {
|
||||
id: client.user.id,
|
||||
tag: client.user.tag,
|
||||
} : null
|
||||
})}`);
|
||||
}
|
||||
clientStatusInterval = setInterval(() => {
|
||||
logClientState("periodic check");
|
||||
}, 10000);
|
||||
client.on('ready', () => {
|
||||
logClientState("on ready event");
|
||||
});
|
||||
client.on('disconnect', () => {
|
||||
logClientState("on disconnect event");
|
||||
if (clientStatusInterval) {
|
||||
clearInterval(clientStatusInterval);
|
||||
clientStatusInterval = null;
|
||||
}
|
||||
});
|
||||
return client;
|
||||
}
|
||||
|
||||
// Configuration from command-line arguments or environment variables
|
||||
const config = {
|
||||
DISCORD_TOKEN: (() => {
|
||||
try {
|
||||
// First check for command-line argument
|
||||
const tokenIndex = process.argv.indexOf('--config');
|
||||
if (tokenIndex !== -1 && tokenIndex + 1 < process.argv.length) {
|
||||
const configArg = process.argv[tokenIndex + 1];
|
||||
if (typeof configArg === 'string') {
|
||||
try {
|
||||
const parsedConfig = JSON.parse(configArg);
|
||||
return parsedConfig.DISCORD_TOKEN;
|
||||
} catch (err) {
|
||||
// If not valid JSON, try using the string directly
|
||||
return configArg;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Then try environment variable
|
||||
return process.env.DISCORD_TOKEN;
|
||||
} catch (err) {
|
||||
process.stderr.write('Error parsing configuration: ' + String(err) + '\n');
|
||||
return null;
|
||||
}
|
||||
})(),
|
||||
TRANSPORT: (() => {
|
||||
// Check for transport type argument
|
||||
const transportIndex = process.argv.indexOf('--transport');
|
||||
if (transportIndex !== -1 && transportIndex + 1 < process.argv.length) {
|
||||
return process.argv[transportIndex + 1];
|
||||
}
|
||||
// Default to stdio
|
||||
return 'stdio';
|
||||
})(),
|
||||
HTTP_PORT: (() => {
|
||||
// Check for port argument
|
||||
const portIndex = process.argv.indexOf('--port');
|
||||
if (portIndex !== -1 && portIndex + 1 < process.argv.length) {
|
||||
return parseInt(process.argv[portIndex + 1]);
|
||||
}
|
||||
// Default port for MCP
|
||||
return 8080;
|
||||
})()
|
||||
};
|
||||
|
||||
const discord = createDiscordClient(config.DISCORD_TOKEN);
|
||||
const server = createMcpServer(discord);
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
// region MCP Streamable HTTP Transport Handlers
|
||||
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
|
||||
const mcpPostHandler = async (req: Request, res: Response) => {
|
||||
const sessionId = req.headers['mcp-session-id'] as string;
|
||||
if (sessionId) {
|
||||
process.stderr.write(`Received MCP request for session: ${sessionId}\n`);
|
||||
} else {
|
||||
process.stderr.write('Request body:' + JSON.stringify(req.body) + '\n');
|
||||
}
|
||||
|
||||
try {
|
||||
let transport: StreamableHTTPServerTransport;
|
||||
if (sessionId && transports[sessionId]) {
|
||||
transport = transports[sessionId];
|
||||
} else if (!sessionId && isInitializeRequest(req.body)) {
|
||||
// New session initialization
|
||||
const eventStore = new InMemoryEventStore();
|
||||
transport = new StreamableHTTPServerTransport({
|
||||
sessionIdGenerator: () => randomUUID(),
|
||||
eventStore,
|
||||
enableJsonResponse: true, // Enable JSON response mode
|
||||
onsessioninitialized: sessionId => {
|
||||
// Store the transport by session ID for future requests
|
||||
transports[sessionId] = transport;
|
||||
process.stderr.write(`Initialized new MCP session: ${sessionId}\n`);
|
||||
}
|
||||
});
|
||||
|
||||
// Set up onclose handler to clean up transport
|
||||
transport.onclose = () => {
|
||||
const sid = transport.sessionId;
|
||||
if (sid && transports[sid]) {
|
||||
delete transports[sid];
|
||||
process.stderr.write(`Closed and removed MCP session: ${sid}\n`);
|
||||
}
|
||||
};
|
||||
|
||||
// Connect the transport to the MCP server before handling the request
|
||||
await server.connect(transport);
|
||||
|
||||
await transport.handleRequest(req, res, req.body);
|
||||
return;
|
||||
} else {
|
||||
// Invalid request without session ID
|
||||
res.status(400).json({
|
||||
jsonrpc: "2.0",
|
||||
error: { code: -32000, message: "Invalid or missing MCP session ID." },
|
||||
id: req.body.id || null
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the request with the existing transport - no need to reconnect
|
||||
await transport.handleRequest(req, res, req.body);
|
||||
} catch (err) {
|
||||
process.stderr.write('Error handling MCP request: ' + String(err) + '\n');
|
||||
if (!res.headersSent) {
|
||||
res.status(500).json({
|
||||
jsonrpc: "2.0",
|
||||
error: { code: -32000, message: "Internal server error." },
|
||||
id: req.body.id || null
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
// Handle GET requests for SSE streams (using built-in support from StreamableHTTP)
|
||||
const mcpGetHandler = async (req: Request, res: Response) => {
|
||||
const sessionId = req.headers['mcp-session-id'] as string | undefined;
|
||||
if (!sessionId || !transports[sessionId]) {
|
||||
res.status(400).send('Invalid or missing session ID');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for Last-Event-ID header for resumability
|
||||
const lastEventId = req.headers['last-event-id'] as string | undefined;
|
||||
if (lastEventId) {
|
||||
process.stderr.write(`Client reconnecting with Last-Event-ID: ${lastEventId}\n`);
|
||||
} else {
|
||||
process.stderr.write(`Establishing new SSE stream for session ${sessionId}\n`);
|
||||
}
|
||||
|
||||
const transport = transports[sessionId];
|
||||
await transport.handleRequest(req, res);
|
||||
};
|
||||
// Handle DELETE requests for session termination (according to MCP spec)
|
||||
const mcpDeleteHandler = async (req: Request, res: Response) => {
|
||||
const sessionId = req.headers['mcp-session-id'] as string | undefined;
|
||||
if (!sessionId || !transports[sessionId]) {
|
||||
res.status(400).send('Invalid or missing session ID');
|
||||
return;
|
||||
}
|
||||
|
||||
process.stderr.write(`Received session termination request for session ${sessionId}\n`);
|
||||
|
||||
try {
|
||||
const transport = transports[sessionId];
|
||||
await transport.handleRequest(req, res);
|
||||
} catch (error) {
|
||||
process.stderr.write('Error handling session termination:' + String(error) + '\n');
|
||||
if (!res.headersSent) {
|
||||
res.status(500).send('Error processing session termination');
|
||||
}
|
||||
}
|
||||
};
|
||||
// endregion
|
||||
|
||||
// Handle both Stdio and HTTP transports based on configuration
|
||||
if (config.TRANSPORT.toLowerCase() === 'http') {
|
||||
// HTTP transport endpoints
|
||||
app.post('/mcp', mcpPostHandler);
|
||||
app.get('/mcp', mcpGetHandler);
|
||||
app.delete('/mcp', mcpDeleteHandler);
|
||||
|
||||
const port = config.HTTP_PORT || 8080;
|
||||
app.listen(port, error => {
|
||||
if (error) {
|
||||
process.stderr.write('Error starting MCP HTTP server: ' + String(error) + '\n');
|
||||
process.exit(1);
|
||||
}
|
||||
process.stderr.write(`MCP Streamable HTTP Server listening on port ${port}\n`);
|
||||
});
|
||||
|
||||
// Handle server shutdown
|
||||
process.on('SIGINT', async () => {
|
||||
process.stderr.write('Shutting down server...\n');
|
||||
|
||||
// Close all active transports to properly clean up resources
|
||||
for (const sessionId in transports) {
|
||||
try {
|
||||
process.stderr.write(`Closing transport for session ${sessionId}\n`);
|
||||
await transports[sessionId].close();
|
||||
delete transports[sessionId];
|
||||
} catch (error) {
|
||||
process.stderr.write(`Error closing transport for session ${sessionId}:` + String(error) + '\n');
|
||||
}
|
||||
}
|
||||
process.stderr.write('Server shutdown complete\n');
|
||||
process.exit(0);
|
||||
});
|
||||
} else {
|
||||
// Stdio transport
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
process.stderr.write('MCP Stdio Server started. Awaiting messages...\n');
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import { Server } from '@modelcontextprotocol/sdk/server'
|
||||
|
||||
export type Level = 'debug' | 'info' | 'notice' | 'warning' | 'error' | 'critical' | 'alert' | 'emergency';
|
||||
|
||||
const levelPriority: Record<Level, number> = {
|
||||
debug: 0,
|
||||
info: 1,
|
||||
notice: 2,
|
||||
warning: 3,
|
||||
error: 4,
|
||||
critical: 5,
|
||||
alert: 6,
|
||||
emergency: 7,
|
||||
};
|
||||
|
||||
export let currentLevel: Level = 'info';
|
||||
|
||||
export function setLevel(level: Level) {
|
||||
if (!levelPriority.hasOwnProperty(level)) return false;
|
||||
currentLevel = level;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function log(server: Server, message: string, level: Level = 'info') {
|
||||
if (levelPriority[level] < levelPriority[currentLevel]) return;
|
||||
server.sendLoggingMessage(
|
||||
{
|
||||
level: level,
|
||||
logger: 'mcp-discord',
|
||||
data: { text: message },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function info(server: Server, message: string) {
|
||||
log(server, message, 'info');
|
||||
}
|
||||
|
||||
export function warning(server: Server, message: string) {
|
||||
log(server, message, 'warning');
|
||||
}
|
||||
|
||||
export function error(server: Server, message: string) {
|
||||
log(server, message, 'error');
|
||||
}
|
||||
212
src/schemas.ts
212
src/schemas.ts
|
|
@ -1,28 +1,38 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export const DiscordLoginSchema = z.object({
|
||||
token: z.string().optional()
|
||||
token: z.string({ description: "The bot token to use for login." }).optional()
|
||||
}, {
|
||||
description: "Login to Discord using a bot token. If no token is provided, the bot will attempt to use the token from the environment variable DISCORD_TOKEN."
|
||||
});
|
||||
|
||||
export const SendMessageSchema = z.object({
|
||||
channelId: z.string(),
|
||||
message: z.string(),
|
||||
replyToMessageId: z.string().optional()
|
||||
channelId: z.string({ description: "The ID of the channel to send the message to." }),
|
||||
message: z.string({ description: "The content of the message to send." }),
|
||||
replyToMessageId: z.string({ description: "The ID of the message to reply to, if any." }).optional()
|
||||
}, {
|
||||
description: "Send a message to a specified channel, optionally as a reply to another message."
|
||||
});
|
||||
|
||||
export const GetForumChannelsSchema = z.object({
|
||||
guildId: z.string()
|
||||
guildId: z.string({ description: "The ID of the server (guild) to get forum channels from." })
|
||||
}, {
|
||||
description: "Get all forum channels in a specified server (guild)."
|
||||
});
|
||||
|
||||
export const CreateForumPostSchema = z.object({
|
||||
forumChannelId: z.string(),
|
||||
title: z.string(),
|
||||
content: z.string(),
|
||||
tags: z.array(z.string()).optional()
|
||||
forumChannelId: z.string({ description: "The ID of the forum channel where the thread will be created." }),
|
||||
title: z.string({ description: "The title of the forum post (thread)." }),
|
||||
content: z.string({ description: "The body content of the forum post." }),
|
||||
tags: z.array(z.string({ description: "A tag to attach to the forum post." })).optional()
|
||||
}, {
|
||||
description: "Create a new forum post (thread) in a specified forum channel."
|
||||
});
|
||||
|
||||
export const GetForumPostSchema = z.object({
|
||||
threadId: z.string()
|
||||
threadId: z.string({ description: "The ID of the forum thread to retrieve." })
|
||||
}, {
|
||||
description: "Get details of a specific forum post (thread) by its ID."
|
||||
});
|
||||
|
||||
export const ListForumThreadsSchema = z.object({
|
||||
|
|
@ -32,128 +42,166 @@ export const ListForumThreadsSchema = z.object({
|
|||
});
|
||||
|
||||
export const ReplyToForumSchema = z.object({
|
||||
threadId: z.string(),
|
||||
message: z.string()
|
||||
threadId: z.string({ description: "The ID of the forum thread to reply to." }),
|
||||
message: z.string({ description: "The content of the reply message." })
|
||||
}, {
|
||||
description: "Reply to a specific forum post (thread) by its ID."
|
||||
});
|
||||
|
||||
export const CreateTextChannelSchema = z.object({
|
||||
guildId: z.string(),
|
||||
channelName: z.string(),
|
||||
topic: z.string().optional(),
|
||||
reason: z.string().optional()
|
||||
guildId: z.string({ description: "The ID of the server (guild) where the text channel will be created." }),
|
||||
channelName: z.string({ description: "The name for the new text channel." }),
|
||||
topic: z.string({ description: "The (optional) topic/description for the channel." }).optional(),
|
||||
reason: z.string({ description: "Optional reason for audit logs when creating the channel." }).optional()
|
||||
}, {
|
||||
description: "Create a new text channel in a specified server (guild)."
|
||||
});
|
||||
|
||||
// Category schemas
|
||||
export const CreateCategorySchema = z.object({
|
||||
guildId: z.string(),
|
||||
name: z.string(),
|
||||
position: z.number().optional(),
|
||||
reason: z.string().optional()
|
||||
guildId: z.string({ description: "The ID of the server (guild) where the category will be created." }),
|
||||
name: z.string({ description: "The name of the category to create." }),
|
||||
position: z.number({ description: "Optional sorting position index for the category." }).optional(),
|
||||
reason: z.string({ description: "Optional reason for audit logs when creating the category." }).optional()
|
||||
}, {
|
||||
description: "Create a new category in a specified server (guild)."
|
||||
});
|
||||
|
||||
export const EditCategorySchema = z.object({
|
||||
categoryId: z.string(),
|
||||
name: z.string().optional(),
|
||||
position: z.number().optional(),
|
||||
reason: z.string().optional()
|
||||
categoryId: z.string({ description: "The ID of the category to edit." }),
|
||||
name: z.string({ description: "New name for the category (optional)." }).optional(),
|
||||
position: z.number({ description: "New position index for the category (optional)." }).optional(),
|
||||
reason: z.string({ description: "Optional reason for audit logs when editing the category." }).optional()
|
||||
}, {
|
||||
description: "Edit an existing category's properties."
|
||||
});
|
||||
|
||||
export const DeleteCategorySchema = z.object({
|
||||
categoryId: z.string(),
|
||||
reason: z.string().optional()
|
||||
categoryId: z.string({ description: "The ID of the category to delete." }),
|
||||
reason: z.string({ description: "Optional reason for audit logs when deleting the category." }).optional()
|
||||
}, {
|
||||
description: "Delete a category by its ID."
|
||||
});
|
||||
|
||||
export const DeleteChannelSchema = z.object({
|
||||
channelId: z.string(),
|
||||
reason: z.string().optional()
|
||||
channelId: z.string({ description: "The ID of the channel to delete." }),
|
||||
reason: z.string({ description: "Optional reason for audit logs when deleting the channel." }).optional()
|
||||
}, {
|
||||
description: "Delete a channel by its ID."
|
||||
});
|
||||
|
||||
export const ReadMessagesSchema = z.object({
|
||||
channelId: z.string(),
|
||||
limit: z.number().min(1).max(100).optional().default(50)
|
||||
channelId: z.string({ description: "The ID of the channel to read messages from." }),
|
||||
limit: z.number({ description: "How many recent messages to fetch (1-100)." }).min(1).max(100).optional().default(50)
|
||||
}, {
|
||||
description: "Read recent messages from a specified channel."
|
||||
});
|
||||
|
||||
export const GetServerInfoSchema = z.object({
|
||||
guildId: z.string()
|
||||
guildId: z.string({ description: "The ID of the server (guild) to get information for." })
|
||||
}, {
|
||||
description: "Get information about a specific server (guild) by its ID."
|
||||
});
|
||||
|
||||
export const AddReactionSchema = z.object({
|
||||
channelId: z.string(),
|
||||
messageId: z.string(),
|
||||
emoji: z.string()
|
||||
channelId: z.string({ description: "The ID of the channel containing the message to react to." }),
|
||||
messageId: z.string({ description: "The ID of the message to add a reaction to." }),
|
||||
emoji: z.string({ description: "The emoji to use for the reaction (unicode or custom)." })
|
||||
}, {
|
||||
description: "Add a reaction to a specific message in a channel."
|
||||
});
|
||||
|
||||
export const AddMultipleReactionsSchema = z.object({
|
||||
channelId: z.string(),
|
||||
messageId: z.string(),
|
||||
emojis: z.array(z.string())
|
||||
channelId: z.string({ description: "The ID of the channel containing the message to react to." }),
|
||||
messageId: z.string({ description: "The ID of the message to add reactions to." }),
|
||||
emojis: z.array(z.string({ description: "An emoji to add (unicode or custom)." }))
|
||||
}, {
|
||||
description: "Add multiple reactions to a specific message in a channel."
|
||||
});
|
||||
|
||||
export const RemoveReactionSchema = z.object({
|
||||
channelId: z.string(),
|
||||
messageId: z.string(),
|
||||
emoji: z.string(),
|
||||
userId: z.string().optional()
|
||||
channelId: z.string({ description: "The ID of the channel containing the message to modify reactions on." }),
|
||||
messageId: z.string({ description: "The ID of the message to remove the reaction from." }),
|
||||
emoji: z.string({ description: "The emoji reaction to remove." }),
|
||||
userId: z.string({ description: "Optional ID of the user whose reaction should be removed; if omitted, removes the current bot's reaction." }).optional()
|
||||
}, {
|
||||
description: "Remove a reaction from a specific message in a channel."
|
||||
});
|
||||
|
||||
export const DeleteForumPostSchema = z.object({
|
||||
threadId: z.string(),
|
||||
reason: z.string().optional()
|
||||
threadId: z.string({ description: "The ID of the forum thread to delete." }),
|
||||
reason: z.string({ description: "Optional reason for audit logs when deleting the forum post." }).optional()
|
||||
}, {
|
||||
description: "Delete a forum post (thread) by its ID."
|
||||
});
|
||||
|
||||
export const DeleteMessageSchema = z.object({
|
||||
channelId: z.string(),
|
||||
messageId: z.string(),
|
||||
reason: z.string().optional()
|
||||
channelId: z.string({ description: "The ID of the channel containing the message to delete." }),
|
||||
messageId: z.string({ description: "The ID of the message to delete." }),
|
||||
reason: z.string({ description: "Optional reason for audit logs when deleting the message." }).optional()
|
||||
}, {
|
||||
description: "Delete a message by its ID in a specified channel."
|
||||
});
|
||||
|
||||
export const CreateWebhookSchema = z.object({
|
||||
channelId: z.string(),
|
||||
name: z.string(),
|
||||
avatar: z.string().optional(),
|
||||
reason: z.string().optional()
|
||||
channelId: z.string({ description: "The ID of the channel to create the webhook in." }),
|
||||
name: z.string({ description: "The name to assign to the webhook." }),
|
||||
avatar: z.string({ description: "Optional avatar URL or data for the webhook." }).optional(),
|
||||
reason: z.string({ description: "Optional reason for audit logs when creating the webhook." }).optional()
|
||||
}, {
|
||||
description: "Create a webhook in a specified channel."
|
||||
});
|
||||
|
||||
export const SendWebhookMessageSchema = z.object({
|
||||
webhookId: z.string(),
|
||||
webhookToken: z.string(),
|
||||
content: z.string(),
|
||||
username: z.string().optional(),
|
||||
avatarURL: z.string().optional(),
|
||||
threadId: z.string().optional()
|
||||
webhookId: z.string({ description: "The ID of the webhook to send the message with." }),
|
||||
webhookToken: z.string({ description: "The token for the webhook (used for authentication)." }),
|
||||
content: z.string({ description: "The message content to send via the webhook." }),
|
||||
username: z.string({ description: "Optional username to display for the webhook message." }).optional(),
|
||||
avatarURL: z.string({ description: "Optional avatar URL to display for the webhook message." }).optional(),
|
||||
threadId: z.string({ description: "Optional ID of the thread to post the webhook message into." }).optional()
|
||||
}, {
|
||||
description: "Send a message using a webhook."
|
||||
});
|
||||
|
||||
export const EditWebhookSchema = z.object({
|
||||
webhookId: z.string(),
|
||||
webhookToken: z.string().optional(),
|
||||
name: z.string().optional(),
|
||||
avatar: z.string().optional(),
|
||||
channelId: z.string().optional(),
|
||||
reason: z.string().optional()
|
||||
webhookId: z.string({ description: "The ID of the webhook to edit." }),
|
||||
webhookToken: z.string({ description: "Optional token for the webhook if required to authorize edits." }).optional(),
|
||||
name: z.string({ description: "Optional new name for the webhook." }).optional(),
|
||||
avatar: z.string({ description: "Optional new avatar URL or data for the webhook." }).optional(),
|
||||
channelId: z.string({ description: "Optional channel ID to move the webhook to." }).optional(),
|
||||
reason: z.string({ description: "Optional reason for audit logs when editing the webhook." }).optional()
|
||||
}, {
|
||||
description: "Edit a webhook's properties."
|
||||
});
|
||||
|
||||
export const DeleteWebhookSchema = z.object({
|
||||
webhookId: z.string(),
|
||||
webhookToken: z.string().optional(),
|
||||
reason: z.string().optional()
|
||||
webhookId: z.string({ description: "The ID of the webhook to delete." }),
|
||||
webhookToken: z.string({ description: "Optional token for the webhook if required for deletion." }).optional(),
|
||||
reason: z.string({ description: "Optional reason for audit logs when deleting the webhook." }).optional()
|
||||
}, {
|
||||
description: "Delete a webhook by its ID and token."
|
||||
});
|
||||
|
||||
export const ListServersSchema = z.object({});
|
||||
export const ListServersSchema = z.object({}, {
|
||||
description: "List all servers (guilds) the bot is a member of."
|
||||
});
|
||||
|
||||
export const SearchMessagesSchema = z.object({
|
||||
guildId: z.string().min(1, "guildId is required"),
|
||||
// Optional filters
|
||||
content: z.string().optional(),
|
||||
authorId: z.string().optional(),
|
||||
mentions: z.string().optional(),
|
||||
has: z.enum(['link','embed','file','poll','image','video','sound','sticker','snapshot']).optional(),
|
||||
maxId: z.string().optional(),
|
||||
minId: z.string().optional(),
|
||||
channelId: z.string().optional(),
|
||||
pinned: z.boolean().optional(),
|
||||
authorType: z.enum(['user','bot','webhook']).optional(),
|
||||
sortBy: z.enum(['timestamp','relevance']).optional(),
|
||||
sortOrder: z.enum(['desc','asc']).optional(),
|
||||
limit: z.number().min(1).max(100).default(25).optional(),
|
||||
offset: z.number().min(0).default(0).optional()
|
||||
});
|
||||
guildId: z.string({ description: "The ID of the server (guild) to search in." }).min(1, "guildId is required"),
|
||||
// Optional filters
|
||||
content: z.string({ description: "Search for messages that contain this text." }).optional(),
|
||||
authorId: z.string({ description: "Filter messages to those authored by this user ID." }).optional(),
|
||||
mentions: z.string({ description: "Filter messages that mention a specific user or role ID." }).optional(),
|
||||
has: z.enum(['link', 'embed', 'file', 'poll', 'image', 'video', 'sound', 'sticker', 'snapshot'], { description: "Filter messages that contain a specific type of content." }).optional(),
|
||||
maxId: z.string({ description: "Only include messages with IDs less than or equal to this (pagination)." }).optional(),
|
||||
minId: z.string({ description: "Only include messages with IDs greater than or equal to this (pagination)." }).optional(),
|
||||
channelId: z.string({ description: "If provided, restrict search to a specific channel ID." }).optional(),
|
||||
pinned: z.boolean({ description: "If true, only include pinned messages; if false, only include unpinned; if omitted, include both." }).optional(),
|
||||
authorType: z.enum(['user', 'bot', 'webhook'], { description: "Filter by the type of author (user, bot, or webhook)." }).optional(),
|
||||
sortBy: z.enum(['timestamp', 'relevance'], { description: "Field to sort search results by." }).optional(),
|
||||
sortOrder: z.enum(['desc', 'asc'], { description: "Sort direction for results." }).optional(),
|
||||
limit: z.number({ description: "Number of results to return (1-100)." }).min(1).max(100).default(25).optional(),
|
||||
offset: z.number({ description: "Number of results to skip (for pagination)." }).min(0).default(0).optional()
|
||||
}, {
|
||||
description: "Search messages in a server with various filters."
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue