Sourcebot MCP (#292)

This commit is contained in:
Brendan Kellam 2025-05-07 16:21:05 -07:00 committed by GitHub
parent fb141422af
commit 873c9ef2a4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 1458 additions and 91 deletions

View file

@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- Added special `*` value for `rev:` to allow searching across all branches. [#281](https://github.com/sourcebot-dev/sourcebot/pull/281) - Added special `*` value for `rev:` to allow searching across all branches. [#281](https://github.com/sourcebot-dev/sourcebot/pull/281)
- Added the Sourcebot Model Context Protocol (MCP) server in [packages/mcp](./packages/mcp/README.md) to allow LLMs to interface with Sourcebot. Checkout the npm package [here](https://www.npmjs.com/package/@sourcebot/mcp). [#292](https://github.com/sourcebot-dev/sourcebot/pull/292)
## [3.1.2] - 2025-04-30 ## [3.1.2] - 2025-04-30

View file

@ -29,6 +29,8 @@ clean:
packages/crypto/dist \ packages/crypto/dist \
packages/error/node_modules \ packages/error/node_modules \
packages/error/dist \ packages/error/dist \
packages/mcp/node_modules \
packages/mcp/dist \
.sourcebot .sourcebot
soft-reset: soft-reset:

View file

@ -48,7 +48,9 @@
"pages": [ "pages": [
"docs/more/syntax-reference", "docs/more/syntax-reference",
"docs/more/multi-branch-indexing", "docs/more/multi-branch-indexing",
"docs/more/roles-and-permissions" "docs/more/roles-and-permissions",
"docs/more/mcp-server",
"docs/more/search-contexts"
] ]
} }
] ]
@ -71,8 +73,7 @@
"self-hosting/more/authentication", "self-hosting/more/authentication",
"self-hosting/more/tenancy", "self-hosting/more/tenancy",
"self-hosting/more/transactional-emails", "self-hosting/more/transactional-emails",
"self-hosting/more/declarative-config", "self-hosting/more/declarative-config"
"self-hosting/more/search-contexts"
] ]
}, },
{ {

View file

@ -0,0 +1,181 @@
---
title: Sourcebot MCP server (@sourcebot/mcp)
sidebarTitle: Sourcebot MCP server
---
<Note>
This feature is only available when [self-hosting](/self-hosting) with [authentication](/self-hosting/more/authentication) disabled.
</Note>
The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) is a open standard for providing context to LLMs. The [@sourcebot/mcp](https://www.npmjs.com/package/@sourcebot/mcp) package is a MCP server that enables LLMs to interface with your Sourcebot instance, enabling MCP clients like Cursor, Vscode, and others to have context over your entire codebase.
## Getting Started
<Steps>
<Step title="Launch Sourcebot">
Follow the self-hosting [quick start guide](/self-hosting/overview#quick-start-guide) to launch Sourcebot and get your code indexed. The host url of your instance (e.g., `http://localhost:3000`) is passed to the MCP server via the `SOURCEBOT_HOST` url.
If a host is not provided, then the server will fallback to using the demo instance hosted at https://demo.sourcebot.dev. You can see the list of repositories indexed [here](https://demo.sourcebot.dev/~/repos). Add additional repositories by [opening a PR](https://github.com/sourcebot-dev/sourcebot/blob/main/demo-site-config.json).
</Step>
<Step title="Install the MCP server">
<Note>
Ensure you have [Node.js](https://nodejs.org/en) >= v18.0.0 installed.
</Note>
Next, we can install the [@sourcebot/mcp](https://www.npmjs.com/package/@sourcebot/mcp) MCP server into any supported MCP client:
<AccordionGroup>
<Accordion title="Cursor">
[Cursor MCP docs](https://docs.cursor.com/context/model-context-protocol)
Go to: `Settings` -> `Cursor Settings` -> `MCP` -> `Add new global MCP server`
Paste the following into your `~/.cursor/mcp.json` file. This will install Sourcebot globally within Cursor:
```json
{
"mcpServers": {
"sourcebot": {
"command": "npx",
"args": ["-y", "@sourcebot/mcp@latest" ],
"env": {
"SOURCEBOT_HOST": "http://localhost:3000"
}
}
}
}
```
Replace `http://localhost:3000` with wherever your Sourcebot instance is hosted.
</Accordion>
<Accordion title="Windsurf">
[Windsurf MCP docs](https://docs.windsurf.com/windsurf/mcp)
Go to: `Windsurf Settings` -> `Cascade` -> `Add Server` -> `Add Custom Server`
Paste the following into your `mcp_config.json` file:
```json
{
"mcpServers": {
"sourcebot": {
"command": "npx",
"args": ["-y", "@sourcebot/mcp@latest" ],
"env": {
"SOURCEBOT_HOST": "http://localhost:3000"
}
}
}
}
```
Replace `http://localhost:3000` with wherever your Sourcebot instance is hosted.
</Accordion>
<Accordion title="VS Code">
[VS Code MCP docs](https://code.visualstudio.com/docs/copilot/chat/mcp-servers)
Add the following to your [settings.json](https://code.visualstudio.com/docs/copilot/chat/mcp-servers):
```json
{
"mcp": {
"servers": {
"sourcebot": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@sourcebot/mcp@latest"]
},
"env": {
"SOURCEBOT_HOST": "http://localhost:3000"
}
}
}
}
```
Replace `http://localhost:3000` with wherever your Sourcebot instance is hosted.
</Accordion>
<Accordion title="Claude Code">
[Claude Code MCP docs](https://docs.anthropic.com/en/docs/claude-code/tutorials#set-up-model-context-protocol-mcp)
Run the following command:
```sh
claude mcp add sourcebot -e SOURCEBOT_HOST=http://localhost:3000 -- npx -y @sourcebot/mcp@latest
```
Replace `http://localhost:3000` with wherever your Sourcebot instance is hosted.
</Accordion>
<Accordion title="Claude Desktop">
[Claude Desktop MCP docs](https://modelcontextprotocol.io/quickstart/user)
Add the following to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"sourcebot": {
"command": "npx",
"args": ["-y", "@sourcebot/mcp@latest"],
"env": {
"SOURCEBOT_HOST": "http://localhost:3000"
}
}
}
}
```
Replace `http://localhost:3000` with wherever your Sourcebot instance is hosted.
</Accordion>
</AccordionGroup>
</Step>
<Step title="Prompt the LLM">
Tell your LLM to `use sourcebot` when prompting.
</Step>
</Steps>
## Available Tools
### `search_code`
Fetches code that matches the provided regex pattern in `query`.
Parameters:
| Name | Required | Description |
|:----------------------|:---------|:----------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | Regex pattern to search for. Escape special characters and spaces with a single backslash (e.g., 'console\.log', 'console\ log'). |
| `filterByRepoIds` | no | Restrict search to specific repository IDs (from 'list_repos'). Leave empty to search all. |
| `filterByLanguages` | no | Restrict search to specific languages (GitHub linguist format, e.g., Python, JavaScript). |
| `caseSensitive` | no | Case sensitive search (default: false). |
| `includeCodeSnippets` | no | Include code snippets in results (default: false). |
| `maxTokens` | no | Max tokens to return (default: env.DEFAULT_MINIMUM_TOKENS). |
### `list_repos`
Lists all repositories indexed by Sourcebot.
### `get_file_source`
Fetches the source code for a given file.
Parameters:
| Name | Required | Description |
|:-------------|:---------|:-----------------------------------------------------------------|
| `fileName` | yes | The file to fetch the source code for. |
| `repoId` | yes | The Sourcebot repository ID. |
## Environment Variables
| Name | Default | Description |
|:-------------------------|:-----------------------|:--------------------------------------------------|
| `SOURCEBOT_HOST` | http://localhost:3000 | URL of your Sourcebot instance. |
| `DEFAULT_MINIMUM_TOKENS` | 10000 | Minimum number of tokens to return in responses. |
| `DEFAULT_MATCHES` | 10000 | Number of code matches to fetch per search. |
| `DEFAULT_CONTEXT_LINES` | 5 | Lines of context to include above/below matches. |

View file

@ -6,7 +6,7 @@ sidebarTitle: Search contexts (EE)
import SearchContextSchema from '/snippets/schemas/v3/searchContext.schema.mdx' import SearchContextSchema from '/snippets/schemas/v3/searchContext.schema.mdx'
<Note> <Note>
This is only available in the Enterprise Edition. Please add your [license key](/self-hosting/license-key) to activate it. This feature is only available when [self-hosting](/self-hosting) with an active Enterprise license. Please add your [license key](/self-hosting/license-key) to activate it.
</Note> </Note>
A **search context** is a user-defined grouping of repositories that helps focus searches on specific areas of your codebase, like frontend, backend, or infrastructure code. Some example queries using search contexts: A **search context** is a user-defined grouping of repositories that helps focus searches on specific areas of your codebase, like frontend, backend, or infrastructure code. Some example queries using search contexts:

View file

@ -6,11 +6,13 @@
"scripts": { "scripts": {
"build": "cross-env SKIP_ENV_VALIDATION=1 yarn workspaces foreach -A run build", "build": "cross-env SKIP_ENV_VALIDATION=1 yarn workspaces foreach -A run build",
"test": "yarn workspaces foreach -A run test", "test": "yarn workspaces foreach -A run test",
"dev": "yarn dev:prisma:migrate:dev && npm-run-all --print-label --parallel dev:zoekt dev:backend dev:web", "dev": "yarn dev:prisma:migrate:dev && npm-run-all --print-label --parallel dev:zoekt dev:backend dev:web watch:mcp watch:schemas",
"with-env": "cross-env PATH=\"$PWD/bin:$PATH\" dotenv -e .env.development -c --", "with-env": "cross-env PATH=\"$PWD/bin:$PATH\" dotenv -e .env.development -c --",
"dev:zoekt": "yarn with-env zoekt-webserver -index .sourcebot/index -rpc", "dev:zoekt": "yarn with-env zoekt-webserver -index .sourcebot/index -rpc",
"dev:backend": "yarn with-env yarn workspace @sourcebot/backend dev:watch", "dev:backend": "yarn with-env yarn workspace @sourcebot/backend dev:watch",
"dev:web": "yarn with-env yarn workspace @sourcebot/web dev", "dev:web": "yarn with-env yarn workspace @sourcebot/web dev",
"watch:mcp": "yarn workspace @sourcebot/mcp build:watch",
"watch:schemas": "yarn workspace @sourcebot/schemas watch",
"dev:prisma:migrate:dev": "yarn with-env yarn workspace @sourcebot/db prisma:migrate:dev", "dev:prisma:migrate:dev": "yarn with-env yarn workspace @sourcebot/db prisma:migrate:dev",
"dev:prisma:studio": "yarn with-env yarn workspace @sourcebot/db prisma:studio", "dev:prisma:studio": "yarn with-env yarn workspace @sourcebot/db prisma:studio",
"dev:prisma:migrate:reset": "yarn with-env yarn workspace @sourcebot/db prisma:migrate:reset", "dev:prisma:migrate:reset": "yarn with-env yarn workspace @sourcebot/db prisma:migrate:reset",

View file

@ -51,6 +51,6 @@
"simple-git": "^3.27.0", "simple-git": "^3.27.0",
"strip-json-comments": "^5.0.1", "strip-json-comments": "^5.0.1",
"winston": "^3.15.0", "winston": "^3.15.0",
"zod": "^3.24.2" "zod": "^3.24.3"
} }
} }

2
packages/mcp/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
dist/
node_modules/

5
packages/mcp/.npmignore Normal file
View file

@ -0,0 +1,5 @@
**/*
!/dist/**
!README.md
!package.json
!CHANGELOG.md

13
packages/mcp/CHANGELOG.md Normal file
View file

@ -0,0 +1,13 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [1.0.0] - 2025-05-07
### Added
- Initial release

219
packages/mcp/README.md Normal file
View file

@ -0,0 +1,219 @@
# Sourcebot MCP - Blazingly fast agentic code search for GitHub, GitLab, BitBucket, and more
[![Sourcebot](https://img.shields.io/badge/Website-sourcebot.dev-blue)](https://sourcebot.dev)
[![GitHub](https://img.shields.io/badge/GitHub-sourcebot--dev%2Fsourcebot-green?logo=github)](https://github.com/sourcebot-dev/sourcebot)
[![Docs](https://img.shields.io/badge/Docs-docs.sourcebot.dev-yellow)](https://docs.sourcebot.dev/docs/more/mcp-server)
[![npm](https://img.shields.io/npm/v/@sourcebot/mcp)](https://www.npmjs.com/package/@sourcebot/mcp)
The Sourcebot MCP server enables precise regular expression code search across repos hosted on [GitHub](https://docs.sourcebot.dev/docs/connections/github), [GitLab](https://docs.sourcebot.dev/docs/connections/gitlab), [BitBucket](https://docs.sourcebot.dev/docs/connections/bitbucket-cloud) and [more](#supported-code-hosts). This unlocks the capability for LLM agents to fetch code context for repositories that aren't checked out locally. Some use cases where precise search on a wider code context can help:
- Enriching responses to user requests:
- _"What repositories are using internal library X?"_
- _"Provide usage examples of the CodeMirror component"_
- _"Where is the `useCodeMirrorTheme` hook defined?"_
- _"Find all usages of `deprecatedApi` across all repos"_
- Improving reasoning ability for existing horizontal agents like AI code review, docs generation, etc.
- _"Find the definitions for all functions in this diff"_
- _"Document what systems depend on this class"_
- Building custom LLM horizontal agents like like compliance auditing agents, migration agents, etc.
- _"Find all instances of hardcoded credentials"_
- _"Identify repositories that depend on this depreacted api"_
## Getting Started
1. Install Node.JS >= v18.0.0.
2. (optional) Spin up a Sourcebot instance by following [this guide](https://docs.sourcebot.dev/self-hosting/overview). The host url of your instance (e.g., `http://localhost:3000`) is passed to the MCP server via the `SOURCEBOT_HOST` url.
If a host is not provided, then the server will fallback to using the demo instance hosted at https://demo.sourcebot.dev. You can see the list of repositories indexed [here](https://demo.sourcebot.dev/~/repos). Add additional repositories by [opening a PR](https://github.com/sourcebot-dev/sourcebot/blob/main/demo-site-config.json).
3. Install `@sourcebot/mcp` into your MCP client:
<details>
<summary>Cursor</summary>
[Cursor MCP docs](https://docs.cursor.com/context/model-context-protocol)
Go to: `Settings` -> `Cursor Settings` -> `MCP` -> `Add new global MCP server`
Paste the following into your `~/.cursor/mcp.json` file. This will install Sourcebot globally within Cursor:
```json
{
"mcpServers": {
"sourcebot": {
"command": "npx",
"args": ["-y", "@sourcebot/mcp@latest" ],
// Optional - if not specified, https://demo.sourcebot.dev is used
"env": {
"SOURCEBOT_HOST": "http://localhost:3000"
}
}
}
}
```
</details>
<details>
<summary>Windsurf</summary>
[Windsurf MCP docs](https://docs.windsurf.com/windsurf/mcp)
Go to: `Windsurf Settings` -> `Cascade` -> `Add Server` -> `Add Custom Server`
Paste the following into your `mcp_config.json` file:
```json
{
"mcpServers": {
"sourcebot": {
"command": "npx",
"args": ["-y", "@sourcebot/mcp@latest" ],
// Optional - if not specified, https://demo.sourcebot.dev is used
"env": {
"SOURCEBOT_HOST": "http://localhost:3000"
}
}
}
}
```
</details>
<details>
<summary>VS Code</summary>
[VS Code MCP docs](https://code.visualstudio.com/docs/copilot/chat/mcp-servers)
Add the following to your [settings.json](https://code.visualstudio.com/docs/copilot/chat/mcp-servers):
```json
{
"mcp": {
"servers": {
"sourcebot": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@sourcebot/mcp@latest"],
// Optional - if not specified, https://demo.sourcebot.dev is used
"env": {
"SOURCEBOT_HOST": "http://localhost:3000"
}
}
}
}
}
```
</details>
<details>
<summary>Claude Code</summary>
[Claude Code MCP docs](https://docs.anthropic.com/en/docs/claude-code/tutorials#set-up-model-context-protocol-mcp)
Run the following command:
```sh
# SOURCEBOT_HOST env var is optional - if not specified,
# https://demo.sourcebot.dev is used.
claude mcp add sourcebot -e SOURCEBOT_HOST=http://localhost:3000 -- npx -y @sourcebot/mcp@latest
```
</details>
<details>
<summary>Claude Desktop</summary>
[Claude Desktop MCP docs](https://modelcontextprotocol.io/quickstart/user)
Add the following to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"sourcebot": {
"command": "npx",
"args": ["-y", "@sourcebot/mcp@latest"],
// Optional - if not specified, https://demo.sourcebot.dev is used
"env": {
"SOURCEBOT_HOST": "http://localhost:3000"
}
}
}
}
```
</details>
<br/>
4. Tell your LLM to `use sourcebot` when prompting.
<br/>
For a more detailed guide, checkout [the docs](https://docs.sourcebot.dev/docs/more/mcp-server).
## Available Tools
### search_code
Fetches code that matches the provided regex pattern in `query`.
<details>
<summary>Parameters</summary>
| Name | Required | Description |
|:----------------------|:---------|:----------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | Regex pattern to search for. Escape special characters and spaces with a single backslash (e.g., 'console\.log', 'console\ log'). |
| `filterByRepoIds` | no | Restrict search to specific repository IDs (from 'list_repos'). Leave empty to search all. |
| `filterByLanguages` | no | Restrict search to specific languages (GitHub linguist format, e.g., Python, JavaScript). |
| `caseSensitive` | no | Case sensitive search (default: false). |
| `includeCodeSnippets` | no | Include code snippets in results (default: false). |
| `maxTokens` | no | Max tokens to return (default: env.DEFAULT_MINIMUM_TOKENS). |
</details>
### list_repos
Lists all repositories indexed by Sourcebot.
### get_file_source
Fetches the source code for a given file.
<details>
<summary>Parameters</summary>
| Name | Required | Description |
|:-------------|:---------|:-----------------------------------------------------------------|
| `fileName` | yes | The file to fetch the source code for. |
| `repoId` | yes | The Sourcebot repository ID. |
</details>
## Supported Code Hosts
Sourcebot supports the following code hosts:
- [GitHub](https://docs.sourcebot.dev/docs/connections/github)
- [GitLab](https://docs.sourcebot.dev/docs/connections/gitlab)
- [Bitbucket Cloud](https://docs.sourcebot.dev/docs/connections/bitbucket-cloud)
- [Bitbucket Data Center](https://docs.sourcebot.dev/docs/connections/bitbucket-data-center)
- [Gitea](https://docs.sourcebot.dev/docs/connections/gitea)
- [Gerrit](https://docs.sourcebot.dev/docs/connections/gerrit)
| Don't see your code host? Open a [GitHub discussion](https://github.com/sourcebot-dev/sourcebot/discussions/categories/ideas).
## Future Work
### Semantic Search
Currently, Sourcebot only supports regex-based code search (powered by [zoekt](https://github.com/sourcegraph/zoekt) under the hood). It is great for scenarios when the agent is searching for is something that is super precise and well-represented in the source code (e.g., a specific function name, a error string, etc.). It is not-so-great for _fuzzy_ searches where the objective is to find some loosely defined _category_ or _concept_ in the code (e.g., find code that verifies JWT tokens). The LLM can approximate this by crafting regex searches that attempt to capture a concept (e.g., it might try a query like `"jwt|token|(verify|validate).*(jwt|token)"`), but often yields sub-optimal search results that aren't related. Tools like Cursor solve this with [embedding models](https://docs.cursor.com/context/codebase-indexing) to capture the semantic meaning of code, allowing for LLMs to search using natural language. We would like to extend Sourcebot to support semantic search and expose this capability over MCP as a tool (e.g., `semantic_search_code` tool). [GitHub Discussion](https://github.com/sourcebot-dev/sourcebot/discussions/297)
### Code Navigation
Another idea is to allow LLMs to traverse abstract syntax trees (ASTs) of a codebase to enable reliable code navigation. This could be packaged as tools like `goto_definition`, `find_all_references`, etc., which could be useful for LLMs to get additional code context. [GitHub Discussion](https://github.com/sourcebot-dev/sourcebot/discussions/296)
### Got an idea?
Open up a [GitHub discussion](https://github.com/sourcebot-dev/sourcebot/discussions/categories/feature-requests)!

41
packages/mcp/package.json Normal file
View file

@ -0,0 +1,41 @@
{
"name": "@sourcebot/mcp",
"version": "1.0.0",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"dev": "node ./dist/index.js",
"build:watch": "tsc-watch --preserveWatchOutput"
},
"devDependencies": {
"@types/express": "^5.0.1",
"@types/node": "^20.0.0",
"tsc-watch": "6.2.1",
"tsx": "^4.0.0",
"typescript": "^5.0.0"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.10.2",
"@t3-oss/env-core": "^0.13.4",
"escape-string-regexp": "^5.0.0",
"express": "^5.1.0",
"zod": "^3.24.3"
},
"bin": {
"sourcebot-mcp": "./dist/index.js"
},
"repository": {
"type": "git",
"url": "https://github.com/sourcebot-dev/sourcebot.git",
"directory": "packages/mcp"
},
"keywords": [
"mcp",
"modelcontextprotocol",
"code-search",
"sourcebot",
"code-intelligence"
]
}

View file

@ -0,0 +1,55 @@
import { env } from './env.js';
import { listRepositoriesResponseSchema, searchResponseSchema, fileSourceResponseSchema } from './schemas.js';
import { FileSourceRequest, FileSourceResponse, ListRepositoriesResponse, SearchRequest, SearchResponse, ServiceError } from './types.js';
import { isServiceError } from './utils.js';
export const search = async (request: SearchRequest): Promise<SearchResponse | ServiceError> => {
console.error(`Executing search request: ${JSON.stringify(request, null, 2)}`);
const result = await fetch(`${env.SOURCEBOT_HOST}/api/search`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Org-Domain': '~'
},
body: JSON.stringify(request)
}).then(response => response.json());
if (isServiceError(result)) {
return result;
}
return searchResponseSchema.parse(result);
}
export const listRepos = async (): Promise<ListRepositoriesResponse | ServiceError> => {
const result = await fetch(`${env.SOURCEBOT_HOST}/api/repos`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Org-Domain': '~'
},
}).then(response => response.json());
if (isServiceError(result)) {
return result;
}
return listRepositoriesResponseSchema.parse(result);
}
export const getFileSource = async (request: FileSourceRequest): Promise<FileSourceResponse | ServiceError> => {
const result = await fetch(`${env.SOURCEBOT_HOST}/api/source`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Org-Domain': '~'
},
body: JSON.stringify(request)
}).then(response => response.json());
if (isServiceError(result)) {
return result;
}
return fileSourceResponseSchema.parse(result);
}

23
packages/mcp/src/env.ts Normal file
View file

@ -0,0 +1,23 @@
import { createEnv } from "@t3-oss/env-core";
import { z } from "zod";
export const numberSchema = z.coerce.number();
const SOURCEBOT_DEMO_HOST = "https://demo.sourcebot.dev";
export const env = createEnv({
server: {
SOURCEBOT_HOST: z.string().url().default(SOURCEBOT_DEMO_HOST),
// The minimum number of tokens to return
DEFAULT_MINIMUM_TOKENS: numberSchema.default(10000),
// The number of matches to fetch from the search API.
DEFAULT_MATCHES: numberSchema.default(10000),
// The number of lines to include above and below a match
DEFAULT_CONTEXT_LINES: numberSchema.default(5),
},
runtimeEnv: process.env,
emptyStringAsUndefined: true,
});

223
packages/mcp/src/index.ts Normal file
View file

@ -0,0 +1,223 @@
#!/usr/bin/env node
// Entry point for the MCP server
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import escapeStringRegexp from 'escape-string-regexp';
import { z } from 'zod';
import { listRepos, search, getFileSource } from './client.js';
import { env, numberSchema } from './env.js';
import { TextContent } from './types.js';
import { base64Decode, isServiceError } from './utils.js';
// Create MCP server
const server = new McpServer({
name: 'sourcebot-mcp-server',
version: '0.1.0',
});
server.tool(
"search_code",
`Fetches code that matches the provided regex pattern in \`query\`. This is NOT a semantic search.
Results are returned as an array of matching files, with the file's URL, repository, and language.
If the \`includeCodeSnippets\` property is true, code snippets containing the matches will be included in the response. Only set this to true if the request requires code snippets (e.g., show me examples where library X is used).
When referencing a file in your response, **ALWAYS** include the file's external URL as a link. This makes it easier for the user to view the file, even if they don't have it locally checked out.
**ONLY USE** the \`filterByRepoIds\` property if the request requires searching a specific repo(s). Otherwise, leave it empty.`,
{
query: z
.string()
.describe(`The regex pattern to search for. RULES:
1. When a regex special character needs to be escaped, ALWAYS use a single backslash (\) (e.g., 'console\.log')
2. **ALWAYS** escape spaces with a single backslash (\) (e.g., 'console\ log')
`),
filterByRepoIds: z
.array(z.string())
.describe(`Scope the search to the provided repositories to the Sourcebot compatible repository IDs. **DO NOT** use this property if you want to search all repositories. **YOU MUST** call 'list_repos' first to obtain the exact repository ID.`)
.optional(),
filterByLanguages: z
.array(z.string())
.describe(`Scope the search to the provided languages. The language MUST be formatted as a GitHub linguist language. Examples: Python, JavaScript, TypeScript, Java, C#, C++, PHP, Go, Rust, Ruby, Swift, Kotlin, Shell, C, Dart, HTML, CSS, PowerShell, SQL, R`)
.optional(),
caseSensitive: z
.boolean()
.describe(`Whether the search should be case sensitive (default: false).`)
.optional(),
includeCodeSnippets: z
.boolean()
.describe(`Whether to include the code snippets in the response (default: false). If false, only the file's URL, repository, and language will be returned. Set to false to get a more concise response.`)
.optional(),
maxTokens: numberSchema
.describe(`The maximum number of tokens to return (default: ${env.DEFAULT_MINIMUM_TOKENS}). Higher values provide more context but consume more tokens. Values less than ${env.DEFAULT_MINIMUM_TOKENS} will be ignored.`)
.transform((val) => (val < env.DEFAULT_MINIMUM_TOKENS ? env.DEFAULT_MINIMUM_TOKENS : val))
.optional(),
},
async ({
query,
filterByRepoIds: repoIds = [],
filterByLanguages: languages = [],
maxTokens = env.DEFAULT_MINIMUM_TOKENS,
includeCodeSnippets = false,
caseSensitive = false,
}) => {
if (repoIds.length > 0) {
query += ` ( repo:${repoIds.map(id => escapeStringRegexp(id)).join(' or repo:')} )`;
}
if (languages.length > 0) {
query += ` ( lang:${languages.join(' or lang:')} )`;
}
if (caseSensitive) {
query += ` case:yes`;
} else {
query += ` case:no`;
}
console.error(`Executing search request: ${query}`);
const response = await search({
query,
matches: env.DEFAULT_MATCHES,
contextLines: env.DEFAULT_CONTEXT_LINES,
});
if (isServiceError(response)) {
return {
content: [{
type: "text",
text: `Error searching code: ${response.message}`,
}],
};
}
if (response.files.length === 0) {
return {
content: [{
type: "text",
text: `No results found for the query: ${query}`,
}],
};
}
const content: TextContent[] = [];
let totalTokens = 0;
let isResponseTruncated = false;
for (const file of response.files) {
const numMatches = file.chunks.reduce(
(acc, chunk) => acc + chunk.matchRanges.length,
0,
);
let text = `file: ${file.url}\nnum_matches: ${numMatches}\nrepository: ${file.repository}\nlanguage: ${file.language}`;
if (includeCodeSnippets) {
const snippets = file.chunks.map(chunk => {
const content = base64Decode(chunk.content);
return `\`\`\`\n${content}\n\`\`\``
}).join('\n');
text += `\n\n${snippets}`;
}
// Rough estimate of the number of tokens in the text
// @see: https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them
const tokens = text.length / 4;
if ((totalTokens + tokens) > maxTokens) {
isResponseTruncated = true;
break;
}
totalTokens += tokens;
content.push({
type: "text",
text,
});
}
if (isResponseTruncated) {
content.push({
type: "text",
text: `The response was truncated because the number of tokens exceeded the maximum limit of ${maxTokens}.`,
});
}
return {
content,
}
}
);
server.tool(
"list_repos",
"Lists all repositories in the organization.",
async () => {
const response = await listRepos();
if (isServiceError(response)) {
return {
content: [{
type: "text",
text: `Error listing repositories: ${response.message}`,
}],
};
}
const content: TextContent[] = response.repos.map(repo => {
return {
type: "text",
text: `id: ${repo.name}\nurl: ${repo.url}`,
}
});
return {
content,
};
}
);
server.tool(
"get_file_source",
"Fetches the source code for a given file.",
{
fileName: z.string().describe("The file to fetch the source code for."),
repoId: z.string().describe("The repository to fetch the source code for. This is the Sourcebot compatible repository ID."),
},
async ({ fileName, repoId }) => {
const response = await getFileSource({
fileName,
repository: repoId,
});
if (isServiceError(response)) {
return {
content: [{
type: "text",
text: `Error fetching file source: ${response.message}`,
}],
};
}
const content: TextContent[] = [{
type: "text",
text: `file: ${fileName}\nrepository: ${repoId}\nlanguage: ${response.language}\nsource:\n${base64Decode(response.source)}`,
}]
return {
content,
};
}
);
const runServer = async () => {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Sourcebot MCP server ready');
}
runServer().catch((error) => {
console.error('Failed to start MCP server:', error);
process.exit(1);
});

111
packages/mcp/src/schemas.ts Normal file
View file

@ -0,0 +1,111 @@
// @NOTE : Please keep this file in sync with @sourcebot/web/src/features/search/schemas.ts
// At some point, we should move these to a shared package...
import { z } from "zod";
export const locationSchema = z.object({
// 0-based byte offset from the beginning of the file
byteOffset: z.number(),
// 1-based line number from the beginning of the file
lineNumber: z.number(),
// 1-based column number (in runes) from the beginning of line
column: z.number(),
});
export const rangeSchema = z.object({
start: locationSchema,
end: locationSchema,
});
export const symbolSchema = z.object({
symbol: z.string(),
kind: z.string(),
});
export const searchRequestSchema = z.object({
// The zoekt query to execute.
query: z.string(),
// The number of matches to return.
matches: z.number(),
// The number of context lines to return.
contextLines: z.number().optional(),
// Whether to return the whole file as part of the response.
whole: z.boolean().optional(),
});
export const searchResponseSchema = z.object({
zoektStats: z.object({
// The duration (in nanoseconds) of the search.
duration: z.number(),
fileCount: z.number(),
matchCount: z.number(),
filesSkipped: z.number(),
contentBytesLoaded: z.number(),
indexBytesLoaded: z.number(),
crashes: z.number(),
shardFilesConsidered: z.number(),
filesConsidered: z.number(),
filesLoaded: z.number(),
shardsScanned: z.number(),
shardsSkipped: z.number(),
shardsSkippedFilter: z.number(),
ngramMatches: z.number(),
ngramLookups: z.number(),
wait: z.number(),
matchTreeConstruction: z.number(),
matchTreeSearch: z.number(),
regexpsConsidered: z.number(),
flushReason: z.number(),
}),
files: z.array(z.object({
fileName: z.object({
// The name of the file
text: z.string(),
// Any matching ranges
matchRanges: z.array(rangeSchema),
}),
repository: z.string(),
language: z.string(),
url: z.string(),
chunks: z.array(z.object({
content: z.string(),
matchRanges: z.array(rangeSchema),
contentStart: locationSchema,
symbols: z.array(z.object({
...symbolSchema.shape,
parent: symbolSchema.optional(),
})).optional(),
})),
branches: z.array(z.string()).optional(),
// Set if `whole` is true.
content: z.string().optional(),
})),
isBranchFilteringEnabled: z.boolean(),
});
export const repositorySchema = z.object({
name: z.string(),
url: z.string(),
branches: z.array(z.string()),
rawConfig: z.record(z.string(), z.string()).optional(),
});
export const listRepositoriesResponseSchema = z.object({
repos: z.array(repositorySchema),
});
export const fileSourceRequestSchema = z.object({
fileName: z.string(),
repository: z.string(),
branch: z.string().optional(),
});
export const fileSourceResponseSchema = z.object({
source: z.string(),
language: z.string(),
});
export const serviceErrorSchema = z.object({
statusCode: z.number(),
errorCode: z.string(),
message: z.string(),
});

32
packages/mcp/src/types.ts Normal file
View file

@ -0,0 +1,32 @@
// @NOTE : Please keep this file in sync with @sourcebot/web/src/features/search/types.ts
// At some point, we should move these to a shared package...
import {
fileSourceResponseSchema,
listRepositoriesResponseSchema,
locationSchema,
searchRequestSchema,
searchResponseSchema,
rangeSchema,
fileSourceRequestSchema,
symbolSchema,
serviceErrorSchema,
} from "./schemas.js";
import { z } from "zod";
export type SearchRequest = z.infer<typeof searchRequestSchema>;
export type SearchResponse = z.infer<typeof searchResponseSchema>;
export type SearchResultRange = z.infer<typeof rangeSchema>;
export type SearchResultLocation = z.infer<typeof locationSchema>;
export type SearchResultFile = SearchResponse["files"][number];
export type SearchResultChunk = SearchResultFile["chunks"][number];
export type SearchSymbol = z.infer<typeof symbolSchema>;
export type ListRepositoriesResponse = z.infer<typeof listRepositoriesResponseSchema>;
export type Repository = ListRepositoriesResponse["repos"][number];
export type FileSourceRequest = z.infer<typeof fileSourceRequestSchema>;
export type FileSourceResponse = z.infer<typeof fileSourceResponseSchema>;
export type TextContent = { type: "text", text: string };
export type ServiceError = z.infer<typeof serviceErrorSchema>;

15
packages/mcp/src/utils.ts Normal file
View file

@ -0,0 +1,15 @@
import { ServiceError } from "./types.js";
// From https://developer.mozilla.org/en-US/docs/Glossary/Base64#the_unicode_problem
export const base64Decode = (base64: string): string => {
const binString = atob(base64);
return Buffer.from(Uint8Array.from(binString, (m) => m.codePointAt(0)!).buffer).toString();
}
export const isServiceError = (data: unknown): data is ServiceError => {
return typeof data === 'object' &&
data !== null &&
'statusCode' in data &&
'errorCode' in data &&
'message' in data;
}

View file

@ -0,0 +1,28 @@
{
"compilerOptions": {
"outDir": "dist",
"incremental": true,
"declaration": true,
"emitDecoratorMetadata": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"module": "Node16",
"moduleResolution": "Node16",
"target": "ES2022",
"noEmitOnError": false,
"noImplicitAny": true,
"noUnusedLocals": false,
"pretty": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"lib": [
"ES2023"
],
"strict": true,
"sourceMap": true,
"inlineSources": true,
},
"include": ["src/index.ts"]
}

View file

@ -5,12 +5,14 @@
"scripts": { "scripts": {
"build": "yarn generate && tsc", "build": "yarn generate && tsc",
"generate": "tsx tools/generate.ts", "generate": "tsx tools/generate.ts",
"watch": "nodemon --watch ../../schemas -e json -x 'yarn generate'",
"postinstall": "yarn build" "postinstall": "yarn build"
}, },
"devDependencies": { "devDependencies": {
"@apidevtools/json-schema-ref-parser": "^11.7.3", "@apidevtools/json-schema-ref-parser": "^11.7.3",
"glob": "^11.0.1", "glob": "^11.0.1",
"json-schema-to-typescript": "^15.0.4", "json-schema-to-typescript": "^15.0.4",
"nodemon": "^3.1.10",
"tsx": "^4.19.2", "tsx": "^4.19.2",
"typescript": "^5.7.3" "typescript": "^5.7.3"
}, },

View file

@ -135,7 +135,7 @@
"tailwind-merge": "^2.5.2", "tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"usehooks-ts": "^3.1.0", "usehooks-ts": "^3.1.0",
"zod": "^3.24.2" "zod": "^3.24.3"
}, },
"devDependencies": { "devDependencies": {
"@types/micromatch": "^4.0.9", "@types/micromatch": "^4.0.9",

View file

@ -13,7 +13,6 @@ interface CodePreviewPanelProps {
onClose: () => void; onClose: () => void;
selectedMatchIndex: number; selectedMatchIndex: number;
onSelectedMatchIndexChange: (index: number) => void; onSelectedMatchIndexChange: (index: number) => void;
repoUrlTemplates: Record<string, string>;
} }
export const CodePreviewPanel = ({ export const CodePreviewPanel = ({
@ -21,7 +20,6 @@ export const CodePreviewPanel = ({
onClose, onClose,
selectedMatchIndex, selectedMatchIndex,
onSelectedMatchIndexChange, onSelectedMatchIndexChange,
repoUrlTemplates,
}: CodePreviewPanelProps) => { }: CodePreviewPanelProps) => {
const domain = useDomain(); const domain = useDomain();
@ -42,45 +40,13 @@ export const CodePreviewPanel = ({
branch, branch,
}, domain) }, domain)
.then(({ source }) => { .then(({ source }) => {
const link = (() => {
const template = repoUrlTemplates[fileMatch.repository];
// This is a hacky parser for templates generated by
// the go text/template package. Example template:
// {{URLJoinPath "https://github.com/sourcebot-dev/sourcebot" "blob" .Version .Path}}
// @see: https://pkg.go.dev/text/template
if (!template || !template.match(/^{{URLJoinPath\s.*}}(\?.+)?$/)) {
return undefined;
}
const url =
template.substring("{{URLJoinPath ".length,template.indexOf("}}"))
.replace(".Version", branch ?? "HEAD")
.replace(".Path", fileMatch.fileName.text)
.split(" ")
.map((part) => {
// remove wrapping quotes
if (part.startsWith("\"")) part = part.substring(1);
if (part.endsWith("\"")) part = part.substring(0, part.length - 1);
return part;
})
.join("/");
const optionalQueryParams =
template.substring(template.indexOf("}}") + 2)
.replace("{{.Version}}", branch ?? "HEAD")
.replace("{{.Path}}", fileMatch.fileName.text);
return url + optionalQueryParams;
})();
const decodedSource = base64Decode(source); const decodedSource = base64Decode(source);
return { return {
content: decodedSource, content: decodedSource,
filepath: fileMatch.fileName.text, filepath: fileMatch.fileName.text,
matches: fileMatch.chunks, matches: fileMatch.chunks,
link: link, link: fileMatch.url,
language: fileMatch.language, language: fileMatch.language,
revision: branch ?? "HEAD", revision: branch ?? "HEAD",
}; };

View file

@ -141,14 +141,13 @@ const SearchPageInternal = () => {
}); });
}, [captureEvent, searchQuery, searchResponse]); }, [captureEvent, searchQuery, searchResponse]);
const { fileMatches, searchDurationMs, totalMatchCount, isBranchFilteringEnabled, repoUrlTemplates } = useMemo(() => { const { fileMatches, searchDurationMs, totalMatchCount, isBranchFilteringEnabled } = useMemo(() => {
if (!searchResponse) { if (!searchResponse) {
return { return {
fileMatches: [], fileMatches: [],
searchDurationMs: 0, searchDurationMs: 0,
totalMatchCount: 0, totalMatchCount: 0,
isBranchFilteringEnabled: false, isBranchFilteringEnabled: false,
repoUrlTemplates: {},
}; };
} }
@ -157,7 +156,6 @@ const SearchPageInternal = () => {
searchDurationMs: Math.round(searchResponse.durationMs), searchDurationMs: Math.round(searchResponse.durationMs),
totalMatchCount: searchResponse.zoektStats.matchCount, totalMatchCount: searchResponse.zoektStats.matchCount,
isBranchFilteringEnabled: searchResponse.isBranchFilteringEnabled, isBranchFilteringEnabled: searchResponse.isBranchFilteringEnabled,
repoUrlTemplates: searchResponse.repoUrlTemplates,
} }
}, [searchResponse]); }, [searchResponse]);
@ -207,7 +205,6 @@ const SearchPageInternal = () => {
isMoreResultsButtonVisible={isMoreResultsButtonVisible} isMoreResultsButtonVisible={isMoreResultsButtonVisible}
onLoadMoreResults={onLoadMoreResults} onLoadMoreResults={onLoadMoreResults}
isBranchFilteringEnabled={isBranchFilteringEnabled} isBranchFilteringEnabled={isBranchFilteringEnabled}
repoUrlTemplates={repoUrlTemplates}
repoMetadata={repoMetadata ?? {}} repoMetadata={repoMetadata ?? {}}
searchDurationMs={searchDurationMs} searchDurationMs={searchDurationMs}
numMatches={numMatches} numMatches={numMatches}
@ -222,7 +219,6 @@ interface PanelGroupProps {
isMoreResultsButtonVisible?: boolean; isMoreResultsButtonVisible?: boolean;
onLoadMoreResults: () => void; onLoadMoreResults: () => void;
isBranchFilteringEnabled: boolean; isBranchFilteringEnabled: boolean;
repoUrlTemplates: Record<string, string>;
repoMetadata: Record<string, Repository>; repoMetadata: Record<string, Repository>;
searchDurationMs: number; searchDurationMs: number;
numMatches: number; numMatches: number;
@ -233,7 +229,6 @@ const PanelGroup = ({
isMoreResultsButtonVisible, isMoreResultsButtonVisible,
onLoadMoreResults, onLoadMoreResults,
isBranchFilteringEnabled, isBranchFilteringEnabled,
repoUrlTemplates,
repoMetadata, repoMetadata,
searchDurationMs, searchDurationMs,
numMatches, numMatches,
@ -340,7 +335,6 @@ const PanelGroup = ({
onClose={() => setSelectedFile(undefined)} onClose={() => setSelectedFile(undefined)}
selectedMatchIndex={selectedMatchIndex} selectedMatchIndex={selectedMatchIndex}
onSelectedMatchIndexChange={setSelectedMatchIndex} onSelectedMatchIndexChange={setSelectedMatchIndex}
repoUrlTemplates={repoUrlTemplates}
/> />
</ResizablePanel> </ResizablePanel>
</ResizablePanelGroup> </ResizablePanelGroup>

View file

@ -34,7 +34,6 @@ export const listRepositories = async (orgId: number): Promise<ListRepositoriesR
repos: List.Repos.map((repo) => ({ repos: List.Repos.map((repo) => ({
name: repo.Repository.Name, name: repo.Repository.Name,
url: repo.Repository.URL, url: repo.Repository.URL,
source: repo.Repository.Source,
branches: repo.Repository.Branches?.map((branch) => branch.Name) ?? [], branches: repo.Repository.Branches?.map((branch) => branch.Name) ?? [],
rawConfig: repo.Repository.RawConfig ?? undefined, rawConfig: repo.Repository.RawConfig ?? undefined,
})) }))

View file

@ -1,3 +1,4 @@
// @NOTE : Please keep this file in sync with @sourcebot/mcp/src/schemas.ts
import { z } from "zod"; import { z } from "zod";
export const locationSchema = z.object({ export const locationSchema = z.object({
@ -61,6 +62,7 @@ export const searchResponseSchema = z.object({
// Any matching ranges // Any matching ranges
matchRanges: z.array(rangeSchema), matchRanges: z.array(rangeSchema),
}), }),
url: z.string(),
repository: z.string(), repository: z.string(),
language: z.string(), language: z.string(),
chunks: z.array(z.object({ chunks: z.array(z.object({
@ -76,14 +78,12 @@ export const searchResponseSchema = z.object({
// Set if `whole` is true. // Set if `whole` is true.
content: z.string().optional(), content: z.string().optional(),
})), })),
repoUrlTemplates: z.record(z.string(), z.string()),
isBranchFilteringEnabled: z.boolean(), isBranchFilteringEnabled: z.boolean(),
}); });
export const repositorySchema = z.object({ export const repositorySchema = z.object({
name: z.string(), name: z.string(),
url: z.string(), url: z.string(),
source: z.string(),
branches: z.array(z.string()), branches: z.array(z.string()),
rawConfig: z.record(z.string(), z.string()).optional(), rawConfig: z.record(z.string(), z.string()).optional(),
}); });

View file

@ -7,6 +7,7 @@ import { ErrorCode } from "../../lib/errorCodes";
import { StatusCodes } from "http-status-codes"; import { StatusCodes } from "http-status-codes";
import { zoektSearchResponseSchema } from "./zoektSchema"; import { zoektSearchResponseSchema } from "./zoektSchema";
import { SearchRequest, SearchResponse, SearchResultRange } from "./types"; import { SearchRequest, SearchResponse, SearchResultRange } from "./types";
import assert from "assert";
// List of supported query prefixes in zoekt. // List of supported query prefixes in zoekt.
// @see : https://github.com/sourcebot-dev/zoekt/blob/main/query/parse.go#L417 // @see : https://github.com/sourcebot-dev/zoekt/blob/main/query/parse.go#L417
@ -90,6 +91,36 @@ const transformZoektQuery = async (query: string, orgId: number): Promise<string
return newQueryParts.join(" "); return newQueryParts.join(" ");
} }
// Extracts a repository file URL from a zoekt template, branch, and file name.
function getRepositoryUrl(template: string, branch: string, fileName: string): string {
// This is a hacky parser for templates generated by
// the go text/template package. Example template:
// {{URLJoinPath "https://github.com/sourcebot-dev/sourcebot" "blob" .Version .Path}}
// The template should always match this regex, so let's assert that.
assert(template.match(/^{{URLJoinPath\s.*}}(\?.+)?$/), "Invalid template");
const url =
template.substring("{{URLJoinPath ".length, template.indexOf("}}"))
.replace(".Version", branch)
.replace(".Path", fileName)
.split(" ")
.map((part) => {
// remove wrapping quotes
if (part.startsWith("\"")) part = part.substring(1);
if (part.endsWith("\"")) part = part.substring(0, part.length - 1);
return part;
})
.join("/");
const optionalQueryParams =
template.substring(template.indexOf("}}") + 2)
.replace("{{.Version}}", branch)
.replace("{{.Path}}", fileName);
return encodeURI(url + optionalQueryParams);
}
export const search = async ({ query, matches, contextLines, whole }: SearchRequest, orgId: number) => { export const search = async ({ query, matches, contextLines, whole }: SearchRequest, orgId: number) => {
const transformedQuery = await transformZoektQuery(query, orgId); const transformedQuery = await transformZoektQuery(query, orgId);
if (isServiceError(transformedQuery)) { if (isServiceError(transformedQuery)) {
@ -165,6 +196,15 @@ export const search = async ({ query, matches, contextLines, whole }: SearchRequ
}, },
files: Result.Files?.map((file) => { files: Result.Files?.map((file) => {
const fileNameChunks = file.ChunkMatches.filter((chunk) => chunk.FileName); const fileNameChunks = file.ChunkMatches.filter((chunk) => chunk.FileName);
const template = Result.RepoURLs[file.Repository];
assert(template, `Template not found for repository ${file.Repository}`);
// If there are multiple branches pointing to the same revision of this file, it doesn't
// matter which branch we use here, so use the first one.
const branch = file.Branches && file.Branches.length > 0 ? file.Branches[0] : "HEAD";
const url = getRepositoryUrl(template, branch, file.FileName);
return { return {
fileName: { fileName: {
text: file.FileName, text: file.FileName,
@ -182,6 +222,7 @@ export const search = async ({ query, matches, contextLines, whole }: SearchRequ
})) : [], })) : [],
}, },
repository: file.Repository, repository: file.Repository,
url: url,
language: file.Language, language: file.Language,
chunks: file.ChunkMatches chunks: file.ChunkMatches
.filter((chunk) => !chunk.FileName) // Filter out filename chunks. .filter((chunk) => !chunk.FileName) // Filter out filename chunks.
@ -220,9 +261,7 @@ export const search = async ({ query, matches, contextLines, whole }: SearchRequ
branches: file.Branches, branches: file.Branches,
content: file.Content, content: file.Content,
} }
} }) ?? [],
) ?? [],
repoUrlTemplates: Result.RepoURLs,
isBranchFilteringEnabled: isBranchFilteringEnabled, isBranchFilteringEnabled: isBranchFilteringEnabled,
} satisfies SearchResponse)); } satisfies SearchResponse));

View file

@ -1,3 +1,4 @@
// @NOTE : Please keep this file in sync with @sourcebot/mcp/src/types.ts
import { import {
fileSourceResponseSchema, fileSourceResponseSchema,
listRepositoriesResponseSchema, listRepositoriesResponseSchema,

484
yarn.lock
View file

@ -1970,6 +1970,24 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@modelcontextprotocol/sdk@npm:^1.10.2":
version: 1.10.2
resolution: "@modelcontextprotocol/sdk@npm:1.10.2"
dependencies:
content-type: "npm:^1.0.5"
cors: "npm:^2.8.5"
cross-spawn: "npm:^7.0.3"
eventsource: "npm:^3.0.2"
express: "npm:^5.0.1"
express-rate-limit: "npm:^7.5.0"
pkce-challenge: "npm:^5.0.0"
raw-body: "npm:^3.0.0"
zod: "npm:^3.23.8"
zod-to-json-schema: "npm:^3.24.1"
checksum: 10c0/a2a146dec37d13c8108b4c42912d65f1f5b0e8f3adda4c300336369519f3caa52e996afb65d6a6c03ae3b6fc1e2425cad4af1e619206b6ee3e15327b4ee01d4c
languageName: node
linkType: hard
"@msgpack/msgpack@npm:^2.5.1": "@msgpack/msgpack@npm:^2.5.1":
version: 2.8.0 version: 2.8.0
resolution: "@msgpack/msgpack@npm:2.8.0" resolution: "@msgpack/msgpack@npm:2.8.0"
@ -5188,7 +5206,7 @@ __metadata:
typescript: "npm:^5.6.2" typescript: "npm:^5.6.2"
vitest: "npm:^2.1.9" vitest: "npm:^2.1.9"
winston: "npm:^3.15.0" winston: "npm:^3.15.0"
zod: "npm:^3.24.2" zod: "npm:^3.24.3"
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@ -5226,6 +5244,25 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"@sourcebot/mcp@workspace:packages/mcp":
version: 0.0.0-use.local
resolution: "@sourcebot/mcp@workspace:packages/mcp"
dependencies:
"@modelcontextprotocol/sdk": "npm:^1.10.2"
"@t3-oss/env-core": "npm:^0.13.4"
"@types/express": "npm:^5.0.1"
"@types/node": "npm:^20.0.0"
escape-string-regexp: "npm:^5.0.0"
express: "npm:^5.1.0"
tsc-watch: "npm:6.2.1"
tsx: "npm:^4.0.0"
typescript: "npm:^5.0.0"
zod: "npm:^3.24.3"
bin:
sourcebot-mcp: ./dist/index.js
languageName: unknown
linkType: soft
"@sourcebot/schemas@workspace:*, @sourcebot/schemas@workspace:packages/schemas": "@sourcebot/schemas@workspace:*, @sourcebot/schemas@workspace:packages/schemas":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@sourcebot/schemas@workspace:packages/schemas" resolution: "@sourcebot/schemas@workspace:packages/schemas"
@ -5233,6 +5270,7 @@ __metadata:
"@apidevtools/json-schema-ref-parser": "npm:^11.7.3" "@apidevtools/json-schema-ref-parser": "npm:^11.7.3"
glob: "npm:^11.0.1" glob: "npm:^11.0.1"
json-schema-to-typescript: "npm:^15.0.4" json-schema-to-typescript: "npm:^15.0.4"
nodemon: "npm:^3.1.10"
tsx: "npm:^4.19.2" tsx: "npm:^4.19.2"
typescript: "npm:^5.7.3" typescript: "npm:^5.7.3"
languageName: unknown languageName: unknown
@ -5387,7 +5425,7 @@ __metadata:
usehooks-ts: "npm:^3.1.0" usehooks-ts: "npm:^3.1.0"
vite-tsconfig-paths: "npm:^5.1.3" vite-tsconfig-paths: "npm:^5.1.3"
vitest: "npm:^2.1.5" vitest: "npm:^2.1.5"
zod: "npm:^3.24.2" zod: "npm:^3.24.3"
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@ -5457,6 +5495,25 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@t3-oss/env-core@npm:^0.13.4":
version: 0.13.4
resolution: "@t3-oss/env-core@npm:0.13.4"
peerDependencies:
arktype: ^2.1.0
typescript: ">=5.0.0"
valibot: ^1.0.0-beta.7 || ^1.0.0
zod: ^3.24.0 || ^4.0.0-beta.0
peerDependenciesMeta:
typescript:
optional: true
valibot:
optional: true
zod:
optional: true
checksum: 10c0/3598c1582b4cd0aead095a492d60cb7656ffa308c0362744fe32f04ec6563601c04d898c0da7b5efb4dc7ace1d3b18a77f268a15a9e940a6997d9dd84f86a749
languageName: node
linkType: hard
"@t3-oss/env-nextjs@npm:^0.12.0": "@t3-oss/env-nextjs@npm:^0.12.0":
version: 0.12.0 version: 0.12.0
resolution: "@t3-oss/env-nextjs@npm:0.12.0" resolution: "@t3-oss/env-nextjs@npm:0.12.0"
@ -5610,7 +5667,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/express@npm:^5.0.0": "@types/express@npm:^5.0.0, @types/express@npm:^5.0.1":
version: 5.0.1 version: 5.0.1
resolution: "@types/express@npm:5.0.1" resolution: "@types/express@npm:5.0.1"
dependencies: dependencies:
@ -5710,6 +5767,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/node@npm:^20.0.0":
version: 20.17.32
resolution: "@types/node@npm:20.17.32"
dependencies:
undici-types: "npm:~6.19.2"
checksum: 10c0/2461df36f67704f68db64d33abc5ad00b4b35ac94e996adff88c7322f9572e3e60ddaeed7e9f34ae203120d2ba36cc931fd3a8ddddf0c63943e8600c365c6396
languageName: node
linkType: hard
"@types/nodemailer@npm:^6.4.17": "@types/nodemailer@npm:^6.4.17":
version: 6.4.17 version: 6.4.17
resolution: "@types/nodemailer@npm:6.4.17" resolution: "@types/nodemailer@npm:6.4.17"
@ -6298,6 +6364,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"accepts@npm:^2.0.0":
version: 2.0.0
resolution: "accepts@npm:2.0.0"
dependencies:
mime-types: "npm:^3.0.0"
negotiator: "npm:^1.0.0"
checksum: 10c0/98374742097e140891546076215f90c32644feacf652db48412329de4c2a529178a81aa500fbb13dd3e6cbf6e68d829037b123ac037fc9a08bcec4b87b358eef
languageName: node
linkType: hard
"accepts@npm:~1.3.4, accepts@npm:~1.3.8": "accepts@npm:~1.3.4, accepts@npm:~1.3.8":
version: 1.3.8 version: 1.3.8
resolution: "accepts@npm:1.3.8" resolution: "accepts@npm:1.3.8"
@ -6738,6 +6814,23 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"body-parser@npm:^2.2.0":
version: 2.2.0
resolution: "body-parser@npm:2.2.0"
dependencies:
bytes: "npm:^3.1.2"
content-type: "npm:^1.0.5"
debug: "npm:^4.4.0"
http-errors: "npm:^2.0.0"
iconv-lite: "npm:^0.6.3"
on-finished: "npm:^2.4.1"
qs: "npm:^6.14.0"
raw-body: "npm:^3.0.0"
type-is: "npm:^2.0.0"
checksum: 10c0/a9ded39e71ac9668e2211afa72e82ff86cc5ef94de1250b7d1ba9cc299e4150408aaa5f1e8b03dd4578472a3ce6d1caa2a23b27a6c18e526e48b4595174c116c
languageName: node
linkType: hard
"brace-expansion@npm:^1.1.7": "brace-expansion@npm:^1.1.7":
version: 1.1.11 version: 1.1.11
resolution: "brace-expansion@npm:1.1.11" resolution: "brace-expansion@npm:1.1.11"
@ -6814,7 +6907,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"bytes@npm:3.1.2": "bytes@npm:3.1.2, bytes@npm:^3.1.2":
version: 3.1.2 version: 3.1.2
resolution: "bytes@npm:3.1.2" resolution: "bytes@npm:3.1.2"
checksum: 10c0/76d1c43cbd602794ad8ad2ae94095cddeb1de78c5dddaa7005c51af10b0176c69971a6d88e805a90c2b6550d76636e43c40d8427a808b8645ede885de4a0358e checksum: 10c0/76d1c43cbd602794ad8ad2ae94095cddeb1de78c5dddaa7005c51af10b0176c69971a6d88e805a90c2b6550d76636e43c40d8427a808b8645ede885de4a0358e
@ -6973,7 +7066,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"chokidar@npm:^3.5.3, chokidar@npm:^3.6.0": "chokidar@npm:^3.5.2, chokidar@npm:^3.5.3, chokidar@npm:^3.6.0":
version: 3.6.0 version: 3.6.0
resolution: "chokidar@npm:3.6.0" resolution: "chokidar@npm:3.6.0"
dependencies: dependencies:
@ -7447,7 +7540,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"content-type@npm:~1.0.4, content-type@npm:~1.0.5": "content-disposition@npm:^1.0.0":
version: 1.0.0
resolution: "content-disposition@npm:1.0.0"
dependencies:
safe-buffer: "npm:5.2.1"
checksum: 10c0/c7b1ba0cea2829da0352ebc1b7f14787c73884bc707c8bc2271d9e3bf447b372270d09f5d3980dc5037c749ceef56b9a13fccd0b0001c87c3f12579967e4dd27
languageName: node
linkType: hard
"content-type@npm:^1.0.5, content-type@npm:~1.0.4, content-type@npm:~1.0.5":
version: 1.0.5 version: 1.0.5
resolution: "content-type@npm:1.0.5" resolution: "content-type@npm:1.0.5"
checksum: 10c0/b76ebed15c000aee4678c3707e0860cb6abd4e680a598c0a26e17f0bfae723ec9cc2802f0ff1bc6e4d80603719010431d2231018373d4dde10f9ccff9dadf5af checksum: 10c0/b76ebed15c000aee4678c3707e0860cb6abd4e680a598c0a26e17f0bfae723ec9cc2802f0ff1bc6e4d80603719010431d2231018373d4dde10f9ccff9dadf5af
@ -7468,6 +7570,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"cookie-signature@npm:^1.2.1":
version: 1.2.2
resolution: "cookie-signature@npm:1.2.2"
checksum: 10c0/54e05df1a293b3ce81589b27dddc445f462f6fa6812147c033350cd3561a42bc14481674e05ed14c7bd0ce1e8bb3dc0e40851bad75415733711294ddce0b7bc6
languageName: node
linkType: hard
"cookie@npm:0.7.1": "cookie@npm:0.7.1":
version: 0.7.1 version: 0.7.1
resolution: "cookie@npm:0.7.1" resolution: "cookie@npm:0.7.1"
@ -7475,7 +7584,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"cookie@npm:~0.7.2": "cookie@npm:^0.7.1, cookie@npm:~0.7.2":
version: 0.7.2 version: 0.7.2
resolution: "cookie@npm:0.7.2" resolution: "cookie@npm:0.7.2"
checksum: 10c0/9596e8ccdbf1a3a88ae02cf5ee80c1c50959423e1022e4e60b91dd87c622af1da309253d8abdb258fb5e3eacb4f08e579dc58b4897b8087574eee0fd35dfa5d2 checksum: 10c0/9596e8ccdbf1a3a88ae02cf5ee80c1c50959423e1022e4e60b91dd87c622af1da309253d8abdb258fb5e3eacb4f08e579dc58b4897b8087574eee0fd35dfa5d2
@ -7489,7 +7598,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"cors@npm:~2.8.5": "cors@npm:^2.8.5, cors@npm:~2.8.5":
version: 2.8.5 version: 2.8.5
resolution: "cors@npm:2.8.5" resolution: "cors@npm:2.8.5"
dependencies: dependencies:
@ -7659,7 +7768,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.3.7, debug@npm:^4.4.0": "debug@npm:4, debug@npm:^4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.3.7, debug@npm:^4.4.0":
version: 4.4.0 version: 4.4.0
resolution: "debug@npm:4.4.0" resolution: "debug@npm:4.4.0"
dependencies: dependencies:
@ -7765,7 +7874,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"depd@npm:2.0.0": "depd@npm:2.0.0, depd@npm:^2.0.0":
version: 2.0.0 version: 2.0.0
resolution: "depd@npm:2.0.0" resolution: "depd@npm:2.0.0"
checksum: 10c0/58bd06ec20e19529b06f7ad07ddab60e504d9e0faca4bd23079fac2d279c3594334d736508dc350e06e510aba5e22e4594483b3a6562ce7c17dd797f4cc4ad2c checksum: 10c0/58bd06ec20e19529b06f7ad07ddab60e504d9e0faca4bd23079fac2d279c3594334d736508dc350e06e510aba5e22e4594483b3a6562ce7c17dd797f4cc4ad2c
@ -8036,6 +8145,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"encodeurl@npm:^2.0.0, encodeurl@npm:~2.0.0":
version: 2.0.0
resolution: "encodeurl@npm:2.0.0"
checksum: 10c0/5d317306acb13e6590e28e27924c754163946a2480de11865c991a3a7eed4315cd3fba378b543ca145829569eefe9b899f3d84bb09870f675ae60bc924b01ceb
languageName: node
linkType: hard
"encodeurl@npm:~1.0.2": "encodeurl@npm:~1.0.2":
version: 1.0.2 version: 1.0.2
resolution: "encodeurl@npm:1.0.2" resolution: "encodeurl@npm:1.0.2"
@ -8043,13 +8159,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"encodeurl@npm:~2.0.0":
version: 2.0.0
resolution: "encodeurl@npm:2.0.0"
checksum: 10c0/5d317306acb13e6590e28e27924c754163946a2480de11865c991a3a7eed4315cd3fba378b543ca145829569eefe9b899f3d84bb09870f675ae60bc924b01ceb
languageName: node
linkType: hard
"encoding@npm:^0.1.13": "encoding@npm:^0.1.13":
version: 0.1.13 version: 0.1.13
resolution: "encoding@npm:0.1.13" resolution: "encoding@npm:0.1.13"
@ -8522,7 +8631,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"escape-html@npm:~1.0.3": "escape-html@npm:^1.0.3, escape-html@npm:~1.0.3":
version: 1.0.3 version: 1.0.3
resolution: "escape-html@npm:1.0.3" resolution: "escape-html@npm:1.0.3"
checksum: 10c0/524c739d776b36c3d29fa08a22e03e8824e3b2fd57500e5e44ecf3cc4707c34c60f9ca0781c0e33d191f2991161504c295e98f68c78fe7baa6e57081ec6ac0a3 checksum: 10c0/524c739d776b36c3d29fa08a22e03e8824e3b2fd57500e5e44ecf3cc4707c34c60f9ca0781c0e33d191f2991161504c295e98f68c78fe7baa6e57081ec6ac0a3
@ -8851,7 +8960,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"etag@npm:~1.8.1": "etag@npm:^1.8.1, etag@npm:~1.8.1":
version: 1.8.1 version: 1.8.1
resolution: "etag@npm:1.8.1" resolution: "etag@npm:1.8.1"
checksum: 10c0/12be11ef62fb9817314d790089a0a49fae4e1b50594135dcb8076312b7d7e470884b5100d249b28c18581b7fd52f8b485689ffae22a11ed9ec17377a33a08f84 checksum: 10c0/12be11ef62fb9817314d790089a0a49fae4e1b50594135dcb8076312b7d7e470884b5100d249b28c18581b7fd52f8b485689ffae22a11ed9ec17377a33a08f84
@ -8873,6 +8982,22 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"eventsource-parser@npm:^3.0.1":
version: 3.0.1
resolution: "eventsource-parser@npm:3.0.1"
checksum: 10c0/146ce5ae8325d07645a49bbc54d7ac3aef42f5138bfbbe83d5cf96293b50eab2219926d6cf41eed0a0f90132578089652ba9286a19297662900133a9da6c2fd0
languageName: node
linkType: hard
"eventsource@npm:^3.0.2":
version: 3.0.6
resolution: "eventsource@npm:3.0.6"
dependencies:
eventsource-parser: "npm:^3.0.1"
checksum: 10c0/074d865ea1c7e29e3243f85a13306e89fca2d775b982dca03fa6bfa75c56827fa89cf1ab9e730db24bd6b104cbdcae074f2b37ba498874e9dd9710fbff4979bb
languageName: node
linkType: hard
"expect-type@npm:^1.1.0": "expect-type@npm:^1.1.0":
version: 1.2.0 version: 1.2.0
resolution: "expect-type@npm:1.2.0" resolution: "expect-type@npm:1.2.0"
@ -8887,6 +9012,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"express-rate-limit@npm:^7.5.0":
version: 7.5.0
resolution: "express-rate-limit@npm:7.5.0"
peerDependencies:
express: ^4.11 || 5 || ^5.0.0-beta.1
checksum: 10c0/3e96afa05b4f577395688ede37e0cb19901f20c350b32575fb076f3d25176209fb88d3648151755c232aaf304147c58531f070757978f376e2f08326449299fd
languageName: node
linkType: hard
"express@npm:^4.21.2": "express@npm:^4.21.2":
version: 4.21.2 version: 4.21.2
resolution: "express@npm:4.21.2" resolution: "express@npm:4.21.2"
@ -8926,6 +9060,41 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"express@npm:^5.0.1, express@npm:^5.1.0":
version: 5.1.0
resolution: "express@npm:5.1.0"
dependencies:
accepts: "npm:^2.0.0"
body-parser: "npm:^2.2.0"
content-disposition: "npm:^1.0.0"
content-type: "npm:^1.0.5"
cookie: "npm:^0.7.1"
cookie-signature: "npm:^1.2.1"
debug: "npm:^4.4.0"
encodeurl: "npm:^2.0.0"
escape-html: "npm:^1.0.3"
etag: "npm:^1.8.1"
finalhandler: "npm:^2.1.0"
fresh: "npm:^2.0.0"
http-errors: "npm:^2.0.0"
merge-descriptors: "npm:^2.0.0"
mime-types: "npm:^3.0.0"
on-finished: "npm:^2.4.1"
once: "npm:^1.4.0"
parseurl: "npm:^1.3.3"
proxy-addr: "npm:^2.0.7"
qs: "npm:^6.14.0"
range-parser: "npm:^1.2.1"
router: "npm:^2.2.0"
send: "npm:^1.1.0"
serve-static: "npm:^2.2.0"
statuses: "npm:^2.0.1"
type-is: "npm:^2.0.1"
vary: "npm:^1.1.2"
checksum: 10c0/80ce7c53c5f56887d759b94c3f2283e2e51066c98d4b72a4cc1338e832b77f1e54f30d0239cc10815a0f849bdb753e6a284d2fa48d4ab56faf9c501f55d751d6
languageName: node
linkType: hard
"fast-content-type-parse@npm:^2.0.0": "fast-content-type-parse@npm:^2.0.0":
version: 2.0.1 version: 2.0.1
resolution: "fast-content-type-parse@npm:2.0.1" resolution: "fast-content-type-parse@npm:2.0.1"
@ -9056,6 +9225,20 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"finalhandler@npm:^2.1.0":
version: 2.1.0
resolution: "finalhandler@npm:2.1.0"
dependencies:
debug: "npm:^4.4.0"
encodeurl: "npm:^2.0.0"
escape-html: "npm:^1.0.3"
on-finished: "npm:^2.4.1"
parseurl: "npm:^1.3.3"
statuses: "npm:^2.0.1"
checksum: 10c0/da0bbca6d03873472ee890564eb2183f4ed377f25f3628a0fc9d16dac40bed7b150a0d82ebb77356e4c6d97d2796ad2dba22948b951dddee2c8768b0d1b9fb1f
languageName: node
linkType: hard
"find-up@npm:^5.0.0": "find-up@npm:^5.0.0":
version: 5.0.0 version: 5.0.0
resolution: "find-up@npm:5.0.0" resolution: "find-up@npm:5.0.0"
@ -9153,6 +9336,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"fresh@npm:^2.0.0":
version: 2.0.0
resolution: "fresh@npm:2.0.0"
checksum: 10c0/0557548194cb9a809a435bf92bcfbc20c89e8b5eb38861b73ced36750437251e39a111fc3a18b98531be9dd91fe1411e4969f229dc579ec0251ce6c5d4900bbc
languageName: node
linkType: hard
"from@npm:~0": "from@npm:~0":
version: 0.1.7 version: 0.1.7
resolution: "from@npm:0.1.7" resolution: "from@npm:0.1.7"
@ -9651,7 +9841,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"http-errors@npm:2.0.0": "http-errors@npm:2.0.0, http-errors@npm:^2.0.0":
version: 2.0.0 version: 2.0.0
resolution: "http-errors@npm:2.0.0" resolution: "http-errors@npm:2.0.0"
dependencies: dependencies:
@ -9710,7 +9900,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2": "iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2, iconv-lite@npm:^0.6.3":
version: 0.6.3 version: 0.6.3
resolution: "iconv-lite@npm:0.6.3" resolution: "iconv-lite@npm:0.6.3"
dependencies: dependencies:
@ -9726,6 +9916,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"ignore-by-default@npm:^1.0.1":
version: 1.0.1
resolution: "ignore-by-default@npm:1.0.1"
checksum: 10c0/9ab6e70e80f7cc12735def7ecb5527cfa56ab4e1152cd64d294522827f2dcf1f6d85531241537dc3713544e88dd888f65cb3c49c7b2cddb9009087c75274e533
languageName: node
linkType: hard
"ignore@npm:^5.2.0, ignore@npm:^5.3.1": "ignore@npm:^5.2.0, ignore@npm:^5.3.1":
version: 5.3.2 version: 5.3.2
resolution: "ignore@npm:5.3.2" resolution: "ignore@npm:5.3.2"
@ -10035,6 +10232,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"is-promise@npm:^4.0.0":
version: 4.0.0
resolution: "is-promise@npm:4.0.0"
checksum: 10c0/ebd5c672d73db781ab33ccb155fb9969d6028e37414d609b115cc534654c91ccd061821d5b987eefaa97cf4c62f0b909bb2f04db88306de26e91bfe8ddc01503
languageName: node
linkType: hard
"is-reference@npm:1.2.1": "is-reference@npm:1.2.1":
version: 1.2.1 version: 1.2.1
resolution: "is-reference@npm:1.2.1" resolution: "is-reference@npm:1.2.1"
@ -10783,6 +10987,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"media-typer@npm:^1.1.0":
version: 1.1.0
resolution: "media-typer@npm:1.1.0"
checksum: 10c0/7b4baa40b25964bb90e2121ee489ec38642127e48d0cc2b6baa442688d3fde6262bfdca86d6bbf6ba708784afcac168c06840c71facac70e390f5f759ac121b9
languageName: node
linkType: hard
"memorystream@npm:^0.3.1": "memorystream@npm:^0.3.1":
version: 0.3.1 version: 0.3.1
resolution: "memorystream@npm:0.3.1" resolution: "memorystream@npm:0.3.1"
@ -10797,6 +11008,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"merge-descriptors@npm:^2.0.0":
version: 2.0.0
resolution: "merge-descriptors@npm:2.0.0"
checksum: 10c0/95389b7ced3f9b36fbdcf32eb946dc3dd1774c2fdf164609e55b18d03aa499b12bd3aae3a76c1c7185b96279e9803525550d3eb292b5224866060a288f335cb3
languageName: node
linkType: hard
"merge2@npm:^1.3.0, merge2@npm:^1.4.1": "merge2@npm:^1.3.0, merge2@npm:^1.4.1":
version: 1.4.1 version: 1.4.1
resolution: "merge2@npm:1.4.1" resolution: "merge2@npm:1.4.1"
@ -10870,6 +11088,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"mime-db@npm:^1.54.0":
version: 1.54.0
resolution: "mime-db@npm:1.54.0"
checksum: 10c0/8d907917bc2a90fa2df842cdf5dfeaf509adc15fe0531e07bb2f6ab15992416479015828d6a74200041c492e42cce3ebf78e5ce714388a0a538ea9c53eece284
languageName: node
linkType: hard
"mime-types@npm:2.1.35, mime-types@npm:^2.1.12, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": "mime-types@npm:2.1.35, mime-types@npm:^2.1.12, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34":
version: 2.1.35 version: 2.1.35
resolution: "mime-types@npm:2.1.35" resolution: "mime-types@npm:2.1.35"
@ -10879,6 +11104,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"mime-types@npm:^3.0.0, mime-types@npm:^3.0.1":
version: 3.0.1
resolution: "mime-types@npm:3.0.1"
dependencies:
mime-db: "npm:^1.54.0"
checksum: 10c0/bd8c20d3694548089cf229016124f8f40e6a60bbb600161ae13e45f793a2d5bb40f96bbc61f275836696179c77c1d6bf4967b2a75e0a8ad40fe31f4ed5be4da5
languageName: node
linkType: hard
"mime@npm:1.6.0": "mime@npm:1.6.0":
version: 1.6.0 version: 1.6.0
resolution: "mime@npm:1.6.0" resolution: "mime@npm:1.6.0"
@ -11408,6 +11642,26 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"nodemon@npm:^3.1.10":
version: 3.1.10
resolution: "nodemon@npm:3.1.10"
dependencies:
chokidar: "npm:^3.5.2"
debug: "npm:^4"
ignore-by-default: "npm:^1.0.1"
minimatch: "npm:^3.1.2"
pstree.remy: "npm:^1.1.8"
semver: "npm:^7.5.3"
simple-update-notifier: "npm:^2.0.0"
supports-color: "npm:^5.5.0"
touch: "npm:^3.1.0"
undefsafe: "npm:^2.0.5"
bin:
nodemon: bin/nodemon.js
checksum: 10c0/95b64d647f2c22e85e375b250517b0a4b32c2d2392ad898444e331f70d6b1ab43b17f53a8a1d68d5879ab8401fc6cd6e26f0d2a8736240984f6b5a8435b407c0
languageName: node
linkType: hard
"nopt@npm:^8.0.0": "nopt@npm:^8.0.0":
version: 8.1.0 version: 8.1.0
resolution: "nopt@npm:8.1.0" resolution: "nopt@npm:8.1.0"
@ -11576,7 +11830,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"on-finished@npm:2.4.1": "on-finished@npm:2.4.1, on-finished@npm:^2.4.1":
version: 2.4.1 version: 2.4.1
resolution: "on-finished@npm:2.4.1" resolution: "on-finished@npm:2.4.1"
dependencies: dependencies:
@ -11585,7 +11839,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"once@npm:^1.3.0": "once@npm:^1.3.0, once@npm:^1.4.0":
version: 1.4.0 version: 1.4.0
resolution: "once@npm:1.4.0" resolution: "once@npm:1.4.0"
dependencies: dependencies:
@ -11751,7 +12005,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"parseurl@npm:~1.3.3": "parseurl@npm:^1.3.3, parseurl@npm:~1.3.3":
version: 1.3.3 version: 1.3.3
resolution: "parseurl@npm:1.3.3" resolution: "parseurl@npm:1.3.3"
checksum: 10c0/90dd4760d6f6174adb9f20cf0965ae12e23879b5f5464f38e92fce8073354341e4b3b76fa3d878351efe7d01e617121955284cfd002ab087fba1a0726ec0b4f5 checksum: 10c0/90dd4760d6f6174adb9f20cf0965ae12e23879b5f5464f38e92fce8073354341e4b3b76fa3d878351efe7d01e617121955284cfd002ab087fba1a0726ec0b4f5
@ -11820,6 +12074,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"path-to-regexp@npm:^8.0.0":
version: 8.2.0
resolution: "path-to-regexp@npm:8.2.0"
checksum: 10c0/ef7d0a887b603c0a142fad16ccebdcdc42910f0b14830517c724466ad676107476bba2fe9fffd28fd4c141391ccd42ea426f32bb44c2c82ecaefe10c37b90f5a
languageName: node
linkType: hard
"path-type@npm:^3.0.0": "path-type@npm:^3.0.0":
version: 3.0.0 version: 3.0.0
resolution: "path-type@npm:3.0.0" resolution: "path-type@npm:3.0.0"
@ -11973,6 +12234,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"pkce-challenge@npm:^5.0.0":
version: 5.0.0
resolution: "pkce-challenge@npm:5.0.0"
checksum: 10c0/c6706d627fdbb6f22bf8cc5d60d96d6b6a7bb481399b336a3d3f4e9bfba3e167a2c32f8ec0b5e74be686a0ba3bcc9894865d4c2dd1b91cea4c05dba1f28602c3
languageName: node
linkType: hard
"possible-typed-array-names@npm:^1.0.0": "possible-typed-array-names@npm:^1.0.0":
version: 1.1.0 version: 1.1.0
resolution: "possible-typed-array-names@npm:1.1.0" resolution: "possible-typed-array-names@npm:1.1.0"
@ -12328,7 +12596,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"proxy-addr@npm:~2.0.7": "proxy-addr@npm:^2.0.7, proxy-addr@npm:~2.0.7":
version: 2.0.7 version: 2.0.7
resolution: "proxy-addr@npm:2.0.7" resolution: "proxy-addr@npm:2.0.7"
dependencies: dependencies:
@ -12365,6 +12633,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"pstree.remy@npm:^1.1.8":
version: 1.1.8
resolution: "pstree.remy@npm:1.1.8"
checksum: 10c0/30f78c88ce6393cb3f7834216cb6e282eb83c92ccb227430d4590298ab2811bc4a4745f850a27c5178e79a8f3e316591de0fec87abc19da648c2b3c6eb766d14
languageName: node
linkType: hard
"punycode.js@npm:^2.3.1": "punycode.js@npm:^2.3.1":
version: 2.3.1 version: 2.3.1
resolution: "punycode.js@npm:2.3.1" resolution: "punycode.js@npm:2.3.1"
@ -12388,7 +12663,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"qs@npm:^6.11.0, qs@npm:^6.12.2": "qs@npm:^6.11.0, qs@npm:^6.12.2, qs@npm:^6.14.0":
version: 6.14.0 version: 6.14.0
resolution: "qs@npm:6.14.0" resolution: "qs@npm:6.14.0"
dependencies: dependencies:
@ -12421,7 +12696,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"range-parser@npm:~1.2.1": "range-parser@npm:^1.2.1, range-parser@npm:~1.2.1":
version: 1.2.1 version: 1.2.1
resolution: "range-parser@npm:1.2.1" resolution: "range-parser@npm:1.2.1"
checksum: 10c0/96c032ac2475c8027b7a4e9fe22dc0dfe0f6d90b85e496e0f016fbdb99d6d066de0112e680805075bd989905e2123b3b3d002765149294dce0c1f7f01fcc2ea0 checksum: 10c0/96c032ac2475c8027b7a4e9fe22dc0dfe0f6d90b85e496e0f016fbdb99d6d066de0112e680805075bd989905e2123b3b3d002765149294dce0c1f7f01fcc2ea0
@ -12447,6 +12722,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"raw-body@npm:^3.0.0":
version: 3.0.0
resolution: "raw-body@npm:3.0.0"
dependencies:
bytes: "npm:3.1.2"
http-errors: "npm:2.0.0"
iconv-lite: "npm:0.6.3"
unpipe: "npm:1.0.0"
checksum: 10c0/f8daf4b724064a4811d118745a781ca0fb4676298b8adadfd6591155549cfea0a067523cf7dd3baeb1265fecc9ce5dfb2fc788c12c66b85202a336593ece0f87
languageName: node
linkType: hard
"react-device-detect@npm:^2.2.3": "react-device-detect@npm:^2.2.3":
version: 2.2.3 version: 2.2.3
resolution: "react-device-detect@npm:2.2.3" resolution: "react-device-detect@npm:2.2.3"
@ -13082,6 +13369,19 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"router@npm:^2.2.0":
version: 2.2.0
resolution: "router@npm:2.2.0"
dependencies:
debug: "npm:^4.4.0"
depd: "npm:^2.0.0"
is-promise: "npm:^4.0.0"
parseurl: "npm:^1.3.3"
path-to-regexp: "npm:^8.0.0"
checksum: 10c0/3279de7450c8eae2f6e095e9edacbdeec0abb5cb7249c6e719faa0db2dba43574b4fff5892d9220631c9abaff52dd3cad648cfea2aaace845e1a071915ac8867
languageName: node
linkType: hard
"rrweb-cssom@npm:^0.7.1": "rrweb-cssom@npm:^0.7.1":
version: 0.7.1 version: 0.7.1
resolution: "rrweb-cssom@npm:0.7.1" resolution: "rrweb-cssom@npm:0.7.1"
@ -13247,7 +13547,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"semver@npm:^7.3.5, semver@npm:^7.5.2, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3": "semver@npm:^7.3.5, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3":
version: 7.7.1 version: 7.7.1
resolution: "semver@npm:7.7.1" resolution: "semver@npm:7.7.1"
bin: bin:
@ -13277,6 +13577,25 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"send@npm:^1.1.0, send@npm:^1.2.0":
version: 1.2.0
resolution: "send@npm:1.2.0"
dependencies:
debug: "npm:^4.3.5"
encodeurl: "npm:^2.0.0"
escape-html: "npm:^1.0.3"
etag: "npm:^1.8.1"
fresh: "npm:^2.0.0"
http-errors: "npm:^2.0.0"
mime-types: "npm:^3.0.1"
ms: "npm:^2.1.3"
on-finished: "npm:^2.4.1"
range-parser: "npm:^1.2.1"
statuses: "npm:^2.0.1"
checksum: 10c0/531bcfb5616948d3468d95a1fd0adaeb0c20818ba4a500f439b800ca2117971489e02074ce32796fd64a6772ea3e7235fe0583d8241dbd37a053dc3378eff9a5
languageName: node
linkType: hard
"serialize-error@npm:8.1.0": "serialize-error@npm:8.1.0":
version: 8.1.0 version: 8.1.0
resolution: "serialize-error@npm:8.1.0" resolution: "serialize-error@npm:8.1.0"
@ -13298,6 +13617,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"serve-static@npm:^2.2.0":
version: 2.2.0
resolution: "serve-static@npm:2.2.0"
dependencies:
encodeurl: "npm:^2.0.0"
escape-html: "npm:^1.0.3"
parseurl: "npm:^1.3.3"
send: "npm:^1.2.0"
checksum: 10c0/30e2ed1dbff1984836cfd0c65abf5d3f3f83bcd696c99d2d3c97edbd4e2a3ff4d3f87108a7d713640d290a7b6fe6c15ddcbc61165ab2eaad48ea8d3b52c7f913
languageName: node
linkType: hard
"server-only@npm:^0.0.1": "server-only@npm:^0.0.1":
version: 0.0.1 version: 0.0.1
resolution: "server-only@npm:0.0.1" resolution: "server-only@npm:0.0.1"
@ -13569,6 +13900,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"simple-update-notifier@npm:^2.0.0":
version: 2.0.0
resolution: "simple-update-notifier@npm:2.0.0"
dependencies:
semver: "npm:^7.5.3"
checksum: 10c0/2a00bd03bfbcbf8a737c47ab230d7920f8bfb92d1159d421bdd194479f6d01ebc995d13fbe13d45dace23066a78a3dc6642999b4e3b38b847e6664191575b20c
languageName: node
linkType: hard
"slash@npm:^3.0.0": "slash@npm:^3.0.0":
version: 3.0.0 version: 3.0.0
resolution: "slash@npm:3.0.0" resolution: "slash@npm:3.0.0"
@ -13758,7 +14098,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"statuses@npm:2.0.1": "statuses@npm:2.0.1, statuses@npm:^2.0.1":
version: 2.0.1 version: 2.0.1
resolution: "statuses@npm:2.0.1" resolution: "statuses@npm:2.0.1"
checksum: 10c0/34378b207a1620a24804ce8b5d230fea0c279f00b18a7209646d5d47e419d1cc23e7cbf33a25a1e51ac38973dc2ac2e1e9c647a8e481ef365f77668d72becfd0 checksum: 10c0/34378b207a1620a24804ce8b5d230fea0c279f00b18a7209646d5d47e419d1cc23e7cbf33a25a1e51ac38973dc2ac2e1e9c647a8e481ef365f77668d72becfd0
@ -14025,7 +14365,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"supports-color@npm:^5.3.0": "supports-color@npm:^5.3.0, supports-color@npm:^5.5.0":
version: 5.5.0 version: 5.5.0
resolution: "supports-color@npm:5.5.0" resolution: "supports-color@npm:5.5.0"
dependencies: dependencies:
@ -14254,6 +14594,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"touch@npm:^3.1.0":
version: 3.1.1
resolution: "touch@npm:3.1.1"
bin:
nodetouch: bin/nodetouch.js
checksum: 10c0/d2e4d269a42c846a22a29065b9af0b263de58effc85a1764bb7a2e8fc4b47700e9e2fcbd7eb1f5bffbb7c73d860f93600cef282b93ddac8f0b62321cb498b36e
languageName: node
linkType: hard
"tough-cookie@npm:^5.0.0": "tough-cookie@npm:^5.0.0":
version: 5.1.2 version: 5.1.2
resolution: "tough-cookie@npm:5.1.2" resolution: "tough-cookie@npm:5.1.2"
@ -14318,6 +14667,22 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"tsc-watch@npm:6.2.1":
version: 6.2.1
resolution: "tsc-watch@npm:6.2.1"
dependencies:
cross-spawn: "npm:^7.0.3"
node-cleanup: "npm:^2.1.2"
ps-tree: "npm:^1.2.0"
string-argv: "npm:^0.3.1"
peerDependencies:
typescript: "*"
bin:
tsc-watch: dist/lib/tsc-watch.js
checksum: 10c0/f5fe19e5ac9f4c42a5600c20aee9ff49e282f11813aead65ed58fa11d98a20f5a82bf4f931897270f49f6475dd54e9aab9c46a07c3801b8d237dfbe77bcf1bfc
languageName: node
linkType: hard
"tsc-watch@npm:^6.2.0": "tsc-watch@npm:^6.2.0":
version: 6.3.0 version: 6.3.0
resolution: "tsc-watch@npm:6.3.0" resolution: "tsc-watch@npm:6.3.0"
@ -14367,7 +14732,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"tsx@npm:^4.19.1, tsx@npm:^4.19.2": "tsx@npm:^4.0.0, tsx@npm:^4.19.1, tsx@npm:^4.19.2":
version: 4.19.3 version: 4.19.3
resolution: "tsx@npm:4.19.3" resolution: "tsx@npm:4.19.3"
dependencies: dependencies:
@ -14406,6 +14771,17 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"type-is@npm:^2.0.0, type-is@npm:^2.0.1":
version: 2.0.1
resolution: "type-is@npm:2.0.1"
dependencies:
content-type: "npm:^1.0.5"
media-typer: "npm:^1.1.0"
mime-types: "npm:^3.0.0"
checksum: 10c0/7f7ec0a060b16880bdad36824ab37c26019454b67d73e8a465ed5a3587440fbe158bc765f0da68344498235c877e7dbbb1600beccc94628ed05599d667951b99
languageName: node
linkType: hard
"type-is@npm:~1.6.18": "type-is@npm:~1.6.18":
version: 1.6.18 version: 1.6.18
resolution: "type-is@npm:1.6.18" resolution: "type-is@npm:1.6.18"
@ -14479,6 +14855,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"typescript@npm:^5.0.0":
version: 5.8.3
resolution: "typescript@npm:5.8.3"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 10c0/5f8bb01196e542e64d44db3d16ee0e4063ce4f3e3966df6005f2588e86d91c03e1fb131c2581baf0fb65ee79669eea6e161cd448178986587e9f6844446dbb48
languageName: node
linkType: hard
"typescript@patch:typescript@npm%3A^5#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.6.2#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.7.3#optional!builtin<compat/typescript>": "typescript@patch:typescript@npm%3A^5#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.6.2#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.7.3#optional!builtin<compat/typescript>":
version: 5.8.2 version: 5.8.2
resolution: "typescript@patch:typescript@npm%3A5.8.2#optional!builtin<compat/typescript>::version=5.8.2&hash=5786d5" resolution: "typescript@patch:typescript@npm%3A5.8.2#optional!builtin<compat/typescript>::version=5.8.2&hash=5786d5"
@ -14489,6 +14875,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"typescript@patch:typescript@npm%3A^5.0.0#optional!builtin<compat/typescript>":
version: 5.8.3
resolution: "typescript@patch:typescript@npm%3A5.8.3#optional!builtin<compat/typescript>::version=5.8.3&hash=5786d5"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 10c0/39117e346ff8ebd87ae1510b3a77d5d92dae5a89bde588c747d25da5c146603a99c8ee588c7ef80faaf123d89ed46f6dbd918d534d641083177d5fac38b8a1cb
languageName: node
linkType: hard
"ua-parser-js@npm:^1.0.33": "ua-parser-js@npm:^1.0.33":
version: 1.0.40 version: 1.0.40
resolution: "ua-parser-js@npm:1.0.40" resolution: "ua-parser-js@npm:1.0.40"
@ -14517,6 +14913,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"undefsafe@npm:^2.0.5":
version: 2.0.5
resolution: "undefsafe@npm:2.0.5"
checksum: 10c0/96c0466a5fbf395917974a921d5d4eee67bca4b30d3a31ce7e621e0228c479cf893e783a109af6e14329b52fe2f0cb4108665fad2b87b0018c0df6ac771261d5
languageName: node
linkType: hard
"undici-types@npm:~6.19.2": "undici-types@npm:~6.19.2":
version: 6.19.8 version: 6.19.8
resolution: "undici-types@npm:6.19.8" resolution: "undici-types@npm:6.19.8"
@ -14728,7 +15131,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"vary@npm:^1, vary@npm:~1.1.2": "vary@npm:^1, vary@npm:^1.1.2, vary@npm:~1.1.2":
version: 1.1.2 version: 1.1.2
resolution: "vary@npm:1.1.2" resolution: "vary@npm:1.1.2"
checksum: 10c0/f15d588d79f3675135ba783c91a4083dcd290a2a5be9fcb6514220a1634e23df116847b1cc51f66bfb0644cf9353b2abb7815ae499bab06e46dd33c1a6bf1f4f checksum: 10c0/f15d588d79f3675135ba783c91a4083dcd290a2a5be9fcb6514220a1634e23df116847b1cc51f66bfb0644cf9353b2abb7815ae499bab06e46dd33c1a6bf1f4f
@ -15249,10 +15652,19 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"zod@npm:^3.24.2": "zod-to-json-schema@npm:^3.24.1":
version: 3.24.2 version: 3.24.5
resolution: "zod@npm:3.24.2" resolution: "zod-to-json-schema@npm:3.24.5"
checksum: 10c0/c638c7220150847f13ad90635b3e7d0321b36cce36f3fc6050ed960689594c949c326dfe2c6fa87c14b126ee5d370ccdebd6efb304f41ef5557a4aaca2824565 peerDependencies:
zod: ^3.24.1
checksum: 10c0/0745b94ba53e652d39f262641cdeb2f75d24339fb6076a38ce55bcf53d82dfaea63adf524ebc5f658681005401687f8e9551c4feca7c4c882e123e66091dfb90
languageName: node
linkType: hard
"zod@npm:^3.23.8, zod@npm:^3.24.3":
version: 3.24.3
resolution: "zod@npm:3.24.3"
checksum: 10c0/ab0369810968d0329a1a141e9418e01e5c9c2a4905cbb7cb7f5a131d6e9487596e1400e21eeff24c4a8ee28dacfa5bd6103893765c055b7a98c2006a5a4fc68d
languageName: node languageName: node
linkType: hard linkType: hard