Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import expo.modules.audio.AudioModule
import expo.modules.backgroundfetch.BackgroundFetchModule
import expo.modules.backgroundtask.BackgroundTaskModule
import expo.modules.battery.BatteryModule
import expo.modules.blob.BlobModule
import expo.modules.blur.BlurModule
import expo.modules.brightness.BrightnessModule
import expo.modules.calendar.CalendarModule
Expand All @@ -17,6 +18,7 @@ import expo.modules.constants.ConstantsService
import expo.modules.contacts.ContactsModule
import expo.modules.core.interfaces.Package
import expo.modules.crypto.CryptoModule
import expo.modules.crypto.aes.AesCryptoModule
import expo.modules.device.DeviceModule
import expo.modules.documentpicker.DocumentPickerModule
import expo.modules.easclient.EASClientModule
Expand Down Expand Up @@ -131,9 +133,11 @@ object ExperiencePackagePicker : ModulesProvider {
NotificationChannelGroupManagerModule::class.java to null,
ExpoBackgroundNotificationTasksModule::class.java to null,
// End of Notifications
AesCryptoModule::class.java to null,
BatteryModule::class.java to null,
BackgroundFetchModule::class.java to null,
BackgroundTaskModule::class.java to null,
BlobModule::class.java to null,
BlurModule::class.java to null,
CalendarModule::class.java to null,
CameraViewModule::class.java to null,
Expand Down
1 change: 0 additions & 1 deletion apps/expo-go/android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ expoAutolinking.exclude = [
'expo-maps',
'expo-network-addons',
'expo-splash-screen',
'expo-blob',
'@expo/ui',
'expo-mesh-gradient',
'@expo/app-integrity',
Expand Down
1 change: 0 additions & 1 deletion apps/expo-go/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ target 'Expo Go' do
'expo-network-addons',
'expo-insights',
'expo-splash-screen',
'expo-blob',
'@expo/ui',
'@expo/app-integrity',
'expo-brownfield'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ const data: SectionListData<ImageProps>[] = [
},
],
},
Platform.OS === 'ios' && {
title: 'PSD',
data: [
{
source: 'https://filesamples.com/samples/image/psd/sample_640%C3%97426.psd',
},
],
},
].filter(Boolean) as SectionListData<ImageProps>[];

function keyExtractor(item: any, index: number) {
Expand Down
23 changes: 12 additions & 11 deletions docs/pages/versions/unversioned/sdk/image.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,18 @@ import { Terminal } from '~/ui/components/Snippet';

#### Supported image formats

| Format | Android | iOS | Web |
| :--------: | :---------: | :---------: | :----------------------------------------------------: |
| WebP | <YesIcon /> | <YesIcon /> | <YesIcon /> |
| PNG / APNG | <YesIcon /> | <YesIcon /> | <YesIcon /> |
| AVIF | <YesIcon /> | <YesIcon /> | <YesIcon /> |
| HEIC | <YesIcon /> | <YesIcon /> | <NoIcon /> [not adopted yet](https://caniuse.com/heif) |
| JPEG | <YesIcon /> | <YesIcon /> | <YesIcon /> |
| GIF | <YesIcon /> | <YesIcon /> | <YesIcon /> |
| SVG | <YesIcon /> | <YesIcon /> | <YesIcon /> |
| ICO | <YesIcon /> | <YesIcon /> | <YesIcon /> |
| ICNS | <NoIcon /> | <YesIcon /> | <NoIcon /> |
| Format | Android | iOS | Web |
| :---------------------: | :---------: | :---------: | :----------------------------------------------------: |
| WebP | <YesIcon /> | <YesIcon /> | <YesIcon /> |
| PNG / APNG | <YesIcon /> | <YesIcon /> | <YesIcon /> |
| AVIF | <YesIcon /> | <YesIcon /> | <YesIcon /> |
| HEIC | <YesIcon /> | <YesIcon /> | <NoIcon /> [not adopted yet](https://caniuse.com/heif) |
| JPEG | <YesIcon /> | <YesIcon /> | <YesIcon /> |
| GIF | <YesIcon /> | <YesIcon /> | <YesIcon /> |
| SVG | <YesIcon /> | <YesIcon /> | <YesIcon /> |
| ICO | <YesIcon /> | <YesIcon /> | <YesIcon /> |
| ICNS | <NoIcon /> | <YesIcon /> | <NoIcon /> |
| PSD (composite preview) | <NoIcon /> | <YesIcon /> | <NoIcon /> |

## Installation

Expand Down
1 change: 1 addition & 0 deletions packages/@expo/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
### 🎉 New features

- Switch EXPO_NO_GIT_STATUS to default to true. ([#42026](https://github.com/expo/expo/pull/42026) by [@EvanBacon](https://github.com/EvanBacon))
- Added support for exposing cli command extensions as MCP tools. ([#40826](https://github.com/expo/expo/pull/40826) by [@chrfalch](https://github.com/chrfalch))
- Added support for cli command extension in expo modules ([#39598](https://github.com/expo/expo/pull/39598) by [@chrfalch](https://github.com/chrfalch))
- Add support for server data loaders in development ([#39570](https://github.com/expo/expo/pull/39570) by [@hassankhan](https://github.com/hassankhan))
- Added support for bundling apps with a new error overlay from `@expo/log-box` package ([#39958](https://github.com/expo/expo/pull/39958) by [@krystofwoldrich](https://github.com/krystofwoldrich))
Expand Down
2 changes: 1 addition & 1 deletion packages/@expo/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
}
},
"devDependencies": {
"@expo/mcp-tunnel": "~0.2.1",
"@expo/mcp-tunnel": "~0.2.3",
"@expo/multipart-body-parser": "^1.0.0",
"@expo/ngrok": "4.1.3",
"@graphql-codegen/cli": "^2.16.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export class DevServerManagerActions {

const metroServerOrigin = this.devServerManager.getDefaultDevServer().getJsInspectorBaseUrl();
const plugins = await this.devServerManager.devtoolsPluginManager.queryPluginsAsync();
Log.log();

const menuItems = [
...defaultMenuItems,
...createDevToolsMenuItems(plugins, defaultServerUrl, metroServerOrigin),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class DevToolsPluginCliExtensionExecutor {
const tool = path.join(this.plugin.packageRoot, this.plugin.cliExtensions!.entryPoint);
const child = this.spawnFunc(
'node',
[tool, command, `'${JSON.stringify(args)}'`, `'${metroServerOrigin}'`],
[tool, command, `${JSON.stringify(args)}`, `${metroServerOrigin}`],
{
cwd: this.projectRoot,
env: { ...process.env },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { DevServerManager } from './DevServerManager';
import { DevToolsPluginOutputSchema } from './DevToolsPlugin.schema';
import { DevToolsPluginCliExtensionExecutor } from './DevToolsPluginCliExtensionExecutor';
import { McpServer } from './MCP';
import { createMCPDevToolsExtensionSchema } from './createMCPDevToolsExtensionSchema';
import { Log } from '../../log';

const debug = require('debug')('expo:start:server:devtools:mcp');

export async function addMcpCapabilities(mcpServer: McpServer, devServerManager: DevServerManager) {
const plugins = await devServerManager.devtoolsPluginManager.queryPluginsAsync();

for (const plugin of plugins) {
if (plugin.cliExtensions) {
const commands = (plugin.cliExtensions.commands ?? []).filter((p) =>
p.environments?.includes('mcp')
);
if (commands.length === 0) {
continue;
}

const schema = createMCPDevToolsExtensionSchema(plugin);

debug(
`Installing MCP CLI extension for plugin: ${plugin.packageName} - found ${commands.length} commands`
);

mcpServer.registerTool(
plugin.packageName,
{
title: plugin.packageName,
description: plugin.description,
inputSchema: { parameters: schema },
},
async ({ parameters }) => {
try {
const { command, ...args } = parameters;

const metroServerOrigin = devServerManager
.getDefaultDevServer()
.getJsInspectorBaseUrl();

const results = await new DevToolsPluginCliExtensionExecutor(
plugin,
devServerManager.projectRoot
).execute({ command, args, metroServerOrigin });

const parsedResults = DevToolsPluginOutputSchema.safeParse(results);
if (parsedResults.success === false) {
throw new Error(
`Invalid output from CLI command: ${parsedResults.error.issues
.map((issue) => issue.message)
.join(', ')}`
);
}
return {
content: parsedResults.data
.map((line) => {
const { type } = line;
if (type === 'text') {
return { type, text: line.text, level: line.level, url: line.url };
} else if (line.type === 'image' || line.type === 'audio') {
// We could present this as a resource_link, but it seems not to be well supported in MCP clients,
// so we'll return a text with the link instead.
return {
type: 'text',
text: `${type} resource: ${line.url}${line.text ? ' (' + line.text + ')' : ''}`,
} as const;
}
return null;
})
.filter((line): line is Exclude<typeof line, null> => line !== null),
};
} catch (e: any) {
Log.error('Error executing MCP CLI command:', e);
return {
content: [{ type: 'text', text: `Error executing command: ${e.toString()}` }],
isError: true,
};
}
}
);
}
}
}
Loading
Loading