Initial commit: MCP server for Gitea repository search
Provides tools for searching files and content across all Gitea repositories: - gitea_list_repos, gitea_search_files, gitea_search_content - gitea_get_file, gitea_repo_info, gitea_list_files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules/
|
||||||
|
.env
|
||||||
|
*.log
|
||||||
60
README.md
Normal file
60
README.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# MCP Gitea Search
|
||||||
|
|
||||||
|
A Model Context Protocol (MCP) server for searching files and content across Gitea repositories.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Set environment variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export GITEA_URL="http://your-gitea-server:3000"
|
||||||
|
export GITEA_TOKEN="your-api-token"
|
||||||
|
```
|
||||||
|
|
||||||
|
Or configure in Claude Code's `~/.claude/settings.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"gitea": {
|
||||||
|
"command": "node",
|
||||||
|
"args": ["/path/to/mcp-gitea-search/index.js"],
|
||||||
|
"env": {
|
||||||
|
"GITEA_URL": "http://your-gitea-server:3000",
|
||||||
|
"GITEA_TOKEN": "your-api-token"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Tools
|
||||||
|
|
||||||
|
| Tool | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `gitea_list_repos` | List all accessible repositories |
|
||||||
|
| `gitea_search_files` | Search files by name pattern (regex) |
|
||||||
|
| `gitea_search_content` | Search within file contents |
|
||||||
|
| `gitea_get_file` | Get contents of a specific file |
|
||||||
|
| `gitea_repo_info` | Get repository details |
|
||||||
|
| `gitea_list_files` | List all files in a repo |
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
After configuring in Claude Code:
|
||||||
|
|
||||||
|
- "Search my Gitea for all .csproj files"
|
||||||
|
- "Find files containing 'ConnectionString' across all repos"
|
||||||
|
- "Get the contents of peter/myrepo/appsettings.json"
|
||||||
|
|
||||||
|
## Creating a Gitea API Token
|
||||||
|
|
||||||
|
1. Go to Settings → Applications in your Gitea instance
|
||||||
|
2. Generate a new token with `read:repository` scope
|
||||||
|
3. Copy the token (shown only once)
|
||||||
345
index.js
Executable file
345
index.js
Executable file
@@ -0,0 +1,345 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
||||||
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||||
|
import {
|
||||||
|
CallToolRequestSchema,
|
||||||
|
ListToolsRequestSchema,
|
||||||
|
} from "@modelcontextprotocol/sdk/types.js";
|
||||||
|
|
||||||
|
const GITEA_URL = process.env.GITEA_URL || "http://pfoster.dynu.net:3000";
|
||||||
|
const GITEA_TOKEN = process.env.GITEA_TOKEN || "";
|
||||||
|
|
||||||
|
async function apiGet(endpoint) {
|
||||||
|
const headers = {};
|
||||||
|
if (GITEA_TOKEN) {
|
||||||
|
headers["Authorization"] = `token ${GITEA_TOKEN}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`${GITEA_URL}/api/v1${endpoint}`, { headers });
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`API error: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listRepos() {
|
||||||
|
const allRepos = [];
|
||||||
|
let page = 1;
|
||||||
|
|
||||||
|
while (page < 20) {
|
||||||
|
const data = await apiGet(`/repos/search?limit=50&page=${page}`);
|
||||||
|
if (!data.data || data.data.length === 0) break;
|
||||||
|
allRepos.push(...data.data);
|
||||||
|
page++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return allRepos;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function searchFilesByName(pattern) {
|
||||||
|
const repos = await listRepos();
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
for (const repo of repos) {
|
||||||
|
try {
|
||||||
|
const tree = await apiGet(
|
||||||
|
`/repos/${repo.owner.login}/${repo.name}/git/trees/${repo.default_branch}?recursive=true`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tree.tree) {
|
||||||
|
const regex = new RegExp(pattern, "i");
|
||||||
|
const matches = tree.tree
|
||||||
|
.filter((f) => regex.test(f.path))
|
||||||
|
.map((f) => ({
|
||||||
|
repo: `${repo.owner.login}/${repo.name}`,
|
||||||
|
path: f.path,
|
||||||
|
type: f.type,
|
||||||
|
url: `${GITEA_URL}/${repo.owner.login}/${repo.name}/src/branch/${repo.default_branch}/${f.path}`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
results.push(...matches);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Skip repos we can't access
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function searchFileContents(query) {
|
||||||
|
const repos = await listRepos();
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
for (const repo of repos) {
|
||||||
|
try {
|
||||||
|
// Use Gitea's code search API
|
||||||
|
const data = await apiGet(
|
||||||
|
`/repos/${repo.owner.login}/${repo.name}/search?q=${encodeURIComponent(query)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (data && Array.isArray(data) && data.length > 0) {
|
||||||
|
results.push({
|
||||||
|
repo: `${repo.owner.login}/${repo.name}`,
|
||||||
|
matches: data.map((m) => ({
|
||||||
|
path: m.path,
|
||||||
|
lineNumber: m.line_number,
|
||||||
|
content: m.content?.substring(0, 200),
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Skip repos without code search or access
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getFileContent(repoFullName, filePath) {
|
||||||
|
const [owner, repo] = repoFullName.split("/");
|
||||||
|
const data = await apiGet(
|
||||||
|
`/repos/${owner}/${repo}/contents/${encodeURIComponent(filePath)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (data.content) {
|
||||||
|
return Buffer.from(data.content, "base64").toString("utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getRepoInfo(repoFullName) {
|
||||||
|
const [owner, repo] = repoFullName.split("/");
|
||||||
|
return await apiGet(`/repos/${owner}/${repo}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create MCP server
|
||||||
|
const server = new Server(
|
||||||
|
{
|
||||||
|
name: "mcp-gitea-search",
|
||||||
|
version: "1.0.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
capabilities: {
|
||||||
|
tools: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// List available tools
|
||||||
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||||
|
return {
|
||||||
|
tools: [
|
||||||
|
{
|
||||||
|
name: "gitea_list_repos",
|
||||||
|
description: "List all accessible repositories on the Gitea server",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {},
|
||||||
|
required: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gitea_search_files",
|
||||||
|
description:
|
||||||
|
"Search for files by name pattern (regex) across all repositories",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
pattern: {
|
||||||
|
type: "string",
|
||||||
|
description:
|
||||||
|
"Regex pattern to match file names (e.g., '\\.csproj$' for .csproj files, 'appsettings' for files containing appsettings)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["pattern"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gitea_search_content",
|
||||||
|
description:
|
||||||
|
"Search for content within files across all repositories (requires Gitea code indexer)",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
query: {
|
||||||
|
type: "string",
|
||||||
|
description: "Text to search for in file contents",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["query"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gitea_get_file",
|
||||||
|
description: "Get the contents of a specific file from a repository",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
repo: {
|
||||||
|
type: "string",
|
||||||
|
description: "Repository full name (e.g., 'owner/repo')",
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
type: "string",
|
||||||
|
description: "File path within the repository",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["repo", "path"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gitea_repo_info",
|
||||||
|
description: "Get information about a specific repository",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
repo: {
|
||||||
|
type: "string",
|
||||||
|
description: "Repository full name (e.g., 'owner/repo')",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["repo"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gitea_list_files",
|
||||||
|
description: "List all files in a repository",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
repo: {
|
||||||
|
type: "string",
|
||||||
|
description: "Repository full name (e.g., 'owner/repo')",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["repo"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle tool calls
|
||||||
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||||
|
const { name, arguments: args } = request.params;
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (name) {
|
||||||
|
case "gitea_list_repos": {
|
||||||
|
const repos = await listRepos();
|
||||||
|
const summary = repos.map((r) => ({
|
||||||
|
name: `${r.owner.login}/${r.name}`,
|
||||||
|
description: r.description || "(no description)",
|
||||||
|
stars: r.stars_count,
|
||||||
|
updated: r.updated_at,
|
||||||
|
}));
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: JSON.stringify(summary, null, 2),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "gitea_search_files": {
|
||||||
|
const results = await searchFilesByName(args.pattern);
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text:
|
||||||
|
results.length > 0
|
||||||
|
? JSON.stringify(results, null, 2)
|
||||||
|
: `No files found matching pattern: ${args.pattern}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "gitea_search_content": {
|
||||||
|
const results = await searchFileContents(args.query);
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text:
|
||||||
|
results.length > 0
|
||||||
|
? JSON.stringify(results, null, 2)
|
||||||
|
: `No content found matching: ${args.query}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "gitea_get_file": {
|
||||||
|
const content = await getFileContent(args.repo, args.path);
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: content || "File not found or empty",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "gitea_repo_info": {
|
||||||
|
const info = await getRepoInfo(args.repo);
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: JSON.stringify(info, null, 2),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "gitea_list_files": {
|
||||||
|
const [owner, repo] = args.repo.split("/");
|
||||||
|
const repoInfo = await apiGet(`/repos/${owner}/${repo}`);
|
||||||
|
const tree = await apiGet(
|
||||||
|
`/repos/${owner}/${repo}/git/trees/${repoInfo.default_branch}?recursive=true`
|
||||||
|
);
|
||||||
|
const files = tree.tree
|
||||||
|
?.filter((f) => f.type === "blob")
|
||||||
|
.map((f) => f.path);
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: JSON.stringify(files, null, 2),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown tool: ${name}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: `Error: ${error.message}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
isError: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start server
|
||||||
|
async function main() {
|
||||||
|
const transport = new StdioServerTransport();
|
||||||
|
await server.connect(transport);
|
||||||
|
console.error("Gitea MCP server running on stdio");
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
161
package-lock.json
generated
Normal file
161
package-lock.json
generated
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
{
|
||||||
|
"name": "mcp-gitea-search",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "mcp-gitea-search",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@modelcontextprotocol/sdk": "^0.5.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"mcp-gitea-search": "index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@modelcontextprotocol/sdk": {
|
||||||
|
"version": "0.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-0.5.0.tgz",
|
||||||
|
"integrity": "sha512-RXgulUX6ewvxjAG0kOpLMEdXXWkzWgaoCGaA2CwNW7cQCIphjpJhjpHSiaPdVCnisjRF/0Cm9KWHUuIoeiAblQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"content-type": "^1.0.5",
|
||||||
|
"raw-body": "^3.0.0",
|
||||||
|
"zod": "^3.23.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/bytes": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/content-type": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/depd": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/http-errors": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"depd": "~2.0.0",
|
||||||
|
"inherits": "~2.0.4",
|
||||||
|
"setprototypeof": "~1.2.0",
|
||||||
|
"statuses": "~2.0.2",
|
||||||
|
"toidentifier": "~1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/iconv-lite": {
|
||||||
|
"version": "0.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz",
|
||||||
|
"integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==",
|
||||||
|
"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/inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/raw-body": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"bytes": "~3.1.2",
|
||||||
|
"http-errors": "~2.0.1",
|
||||||
|
"iconv-lite": "~0.7.0",
|
||||||
|
"unpipe": "~1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/setprototypeof": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/statuses": {
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/toidentifier": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/unpipe": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zod": {
|
||||||
|
"version": "3.25.76",
|
||||||
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||||
|
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
package.json
Normal file
16
package.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "mcp-gitea-search",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "MCP server for searching Gitea repositories",
|
||||||
|
"main": "index.js",
|
||||||
|
"type": "module",
|
||||||
|
"bin": {
|
||||||
|
"mcp-gitea-search": "./index.js"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "node index.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@modelcontextprotocol/sdk": "^0.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user